Qt 多线程专栏

本文详细介绍了Qt框架下线程的使用,包括主线程与进程的关系,子线程的创建与管理,以及如何通过信号槽实现异步通信。通过实例展示了如何在子线程中执行耗时操作,避免阻塞UI,并强调了直接调用线程run方法的错误做法。同时,还演示了在线程中进行图像处理的方法,强调了线程安全和图形界面交互的注意事项。
摘要由CSDN通过智能技术生成
  • 主线程就是进程,进程代表4个G的资源空间,主线程退出就意味着进程退出,在Qt中,主线程不能是子线程的父对象,因此必须在线程的析构函数中去delete线程对象。

  • 只要定时器启动,自动触发timeout()信号

    • 类A继承了Tread类,在A中写run方法,就会覆盖掉Thread中的run方法,所以此时调用start方法后,实现的是自己的run方法体里面的代码。start()相当于间接的调用run(),如果我们直接调用子线程的run()方法,其方法还是运行在主线程中,代码在程序中是顺序执行的,所以不会有解决耗时操作的问题。所以不能直接调用线程的run()方法,只有子线程开始了,才会有异步的效果。当thread.start()方法执行了以后,子线程才会执行run()方法,这样的效果和在主线程中直接调用run()方法的效果是截然不同的。

    在这里插入图片描述
    在这里插入图片描述
    • 一个线程的创建基础格式如下: 在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述
    • 用子线程对象发送信号去开启线程函数: 在这里插入图片描述

于是这里就不需要改变了:
- ###

  • 线程处理函数内部不能操作图形界面,但可以通过信号把数据传到主线程。

  • 把自定义线程函数加入到子线程中:

mythread.h:

#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QObject>

class MyThread : public QObject
{
    Q_OBJECT
public:
    explicit MyThread(QObject *parent = nullptr);
    //线程处理函数
    void myTimeout();
    void setFlag(bool flag = true);
signals:
    void mySignal();
private:
    bool isStop;
};

#endif // MYTHREAD_H

widget.h:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <mythread.h>
#include <QThread>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
    void dealSignal();
    void dealClose();
private slots:
    void on_pushButton_2_clicked();

    void on_pushButton_clicked();

signals:
    void startThread();//启动子线程的信号
private:
    Ui::Widget *ui;
    MyThread *myT;
    QThread *thread;
};
#endif // WIDGET_H

mythread.cpp:

#include "mythread.h"
#include <QThread>//睡觉用
MyThread::MyThread(QObject *parent) : QObject(parent)
{
    isStop = false;
}
void MyThread::myTimeout()
{
    while (isStop == false)
    {
        QThread::sleep(1);
        emit mySignal();
        if(true == isStop)
           { break;
        }
    }
}

void MyThread::setFlag(bool flag)
{
    isStop = flag;
}

widget.cpp:

#include "widget.h"
#include "ui_widget.h"
#include <QThread>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
//这里并不是两个线程,myT定义线程函数,thread才是子线程
    //动态分配空间,不能指定父对象,因为子线程是myT的父亲,如果指定父对象,那么就有两个父对象,是不对的,会报错。
    myT = new MyThread();
    //创建子线程
    thread = new QThread(this);
    //把自定义线程函数加入到子线程中
    myT->moveToThread(thread);
    connect(myT,&MyThread::mySignal,this,&Widget::dealSignal);
    connect(this,&Widget::startThread,myT,&MyThread::myTimeout);
    connect(this,&Widget::destroyed,this,&Widget::dealClose);
}

void Widget::dealSignal()
{
    static int i = 0;
    i++;
    ui->lcdNumber->display(i);
}
Widget::~Widget()
{
    delete ui;
}

void Widget::dealClose()
{
    on_pushButton_clicked();
}

void Widget::on_pushButton_2_clicked()
{
    if(thread->isRunning() == true)
    {
        return;
    }
    //启动线程,但是没有启动线程处理函数
    thread->start();
    myT->setFlag(false);
    //不能直接调用线程处理函数,否则导致线程处理函数和主线程在同一个线程,结果是无响应,只能通过信号和槽调用
    emit startThread();
}

void Widget::on_pushButton_clicked()
{
    if(thread->isRunning() == false)
    {
        return;
    }
    myT->setFlag(true);
    thread->quit();
    thread->wait();
}
  • 在线程中画图(注意:通过槽函数触发线程处理函数,通过线程处理函数触发绘图函数,谁触发谁,谁触发谁,思路很重要):

在这里插入图片描述
myThread.h:

#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QObject>
#include <QImage>


class MyThread : public QObject
{
    Q_OBJECT
public:
    explicit MyThread(QObject *parent = nullptr);
//线程处理函数
    void drawImage();
signals:
    void updateImage(QImage);//函数声明形参名称可加可不加
};

#endif // MYTHREAD_H

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QImage>
#include "mythread.h"
#include <QThread>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
    //重写绘图事件
    void paintEvent(QPaintEvent *);
    void getImage(QImage);//槽函数
    void dealClose();//窗口关闭槽函数
private:
    Ui::Widget *ui;
    QImage image;
    MyThread *myT;//自定义线程对象
    QThread *thread;//子线程
};
#endif // WIDGET_H

mythread.cpp:

#include "mythread.h"
#include <QPainter>
#include <QImage>
#include <QPen>
#include <QBrush>
MyThread::MyThread(QObject *parent) : QObject(parent)
{
}

void MyThread::drawImage()
{
    //定义绘图设备
    QImage image(500,500,QImage::Format_ARGB32);//_ARGB32是透明通道
    //定义画家,
    QPainter p(&image);
    //定义画笔对象
    QPen pen;
    pen.setWidth(5);//设置宽度
    //把画笔交给画家
    p.setPen(pen);
    QBrush brush;//定义画刷
    brush.setStyle(Qt::SolidPattern);//设置样式
    brush.setColor(Qt::red);//设置颜色
    //把画刷交给画家
    p.setBrush(brush);
    //定义五个点
    QPoint a[] =
    {
       QPoint(qrand()%500,qrand()%500),
       QPoint(qrand()%500,qrand()%500),
       QPoint(qrand()%500,qrand()%500),
       QPoint(qrand()%500,qrand()%500),
       QPoint(qrand()%500,qrand()%500)
    };
    p.drawPolygon(a,5);//画多边形
    //通过信号发送图片
    emit updateImage(image);
}

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QPainter>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    myT = new MyThread;  //自定义类对象,需要分配空间,不可以指定父对象
    //创建子线程
    thread = new QThread(this);
    //把自定义模块添加到子线程
    myT->moveToThread(thread);
    //启动子线程,但并没有启动线程处理函数
    thread->start();
    //线程处理函数必须通过信号槽调用
    connect(ui->pushButton,&QPushButton::pressed,myT,&MyThread::drawImage);
    connect(myT,&MyThread::updateImage,this,&Widget::getImage);
    connect(this,&Widget::destroyed,this,&Widget::dealClose);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::dealClose()
{
    thread->quit();//推出子线程
    thread->wait();//回收资源
}
void Widget::getImage(QImage temp)
{
    image = temp;
    update();//更新窗口 间接调用paintEvent
}

void Widget::paintEvent(QPaintEvent *)
{
    QPainter p(this);//创建画家,指定绘图设备为窗口
    p.drawImage(50,50,image);//起点坐标,图片有多大画多大
}

在这里插入图片描述

  • 多线程实例:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@会飞的毛毛虫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值