博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
界面编程之QT的线程20180731
阅读量:4968 次
发布时间:2019-06-12

本文共 14030 字,大约阅读时间需要 46 分钟。

/*******************************************************************************************/

一、为什么需要使用线程

图形界面中一旦使用了线程休眠,图形界面就不会刷新(不会动),呈现卡住无响应的状态。

这是由于图形界面中是单线程的

所以  很复杂的数据处理 耗时长的,就需要创建线程。

 

QThread 线程类,

qt中的线程睡眠函数:QThread::sleep();

 

void MyWidget::on_pushButton_clicked()

{

    //如果定时器没有工作

    if(myTimer->isActive() == false)

    {

        myTimer->start(100);

    }

 

    //很复杂的数据处理

    //需要耗时5s

    sleep(5);//图形界面中一旦使用了线程休眠,图形界面就不会刷新(不会动),呈现卡住无响应的状态。

                  //也就是说由于睡眠,导致前面启动了的定时器都不工作

                             

         myTimer->stop();//过了5s后图形界面才会有响应。但是

           //此时是停了定时器,前面是睡眠 定时器也不工作,所以呈现出一直定时器一直不工作的状态

}

 

/*******************************************************************************************/

二、线程

1.Qt4.7以前 线程的使用方法:

1).自定义一个类,继承于QThread

class MyThread : public QThread

{

public:

  void run();//只有这个是线程处理函数(和主线程不在同一个线程),虚函数。

}

 

2).使用自定义的类创建对象,并开启线程

MyThread myThread;

 

//启动线程

//注意不能直接调用run函数,否则还是在主线程里面运行,而不是在新线程里面

myThread.start();//间接调用run

 

3).返回处理结果,告诉处理完

可以在类中定义一个信号,然后在需要(处理好后)的时候发送这个信号,来告诉其他线程。

 

具体见图《线程1》

 

2.实现:

在项目中添加一个继承QThread的类,添加的时候注意由于这个不是控件,所以下拉框选择QObject,然后

再在文件中把基类改为QThread

class MyThread : public QThread

{

    Q_OBJECT

public:

    explicit MyThread(QObject *parent = 0);

 

protected:

    //QThread的虚函数

    //线程处理函数

    //不能直接调用,通过start()间接调用

    void run();

 

signals:

    void isDone();

 

public slots:

};

void MyThread::run()

{

    //很复杂的数据处理

    //需要耗时5s

    sleep(5);

 

    emit isDone();

}

    //分配空间

    thread = new MyThread(this);

 

    //处理线程中的信号,最好用这种传统的方式

    connect(thread, &MyThread::isDone, this, &MyWidget::dealDone);

         //当按窗口右上角x时,窗口触发destroyed(),否则关了窗口线程还会运行

    connect(this, &MyWidget::destroyed, this, &MyWidget::stopThread);

void MyWidget::dealDone()

{

    qDebug() << "it is over";

    myTimer->stop(); //关闭定时器

}

void MyWidget::on_pushButton_clicked()

{

    //如果定时器没有工作

    if(myTimer->isActive() == false)

    {

        myTimer->start(100);

    }

    //启动线程,处理数据

    thread->start();

}

void MyWidget::stopThread()

{

    //停止线程,不是立马关闭,释放线程占用的内存,线程号等资源。和下面配合使用

    thread->quit();//类似linux中,pthread_exit()

    //等待线程处理完手头动作

    thread->wait();//类似linux中,pthread_join , pthread_detach,

        

         //terminate 强制结束,很暴力,往往会导致内存问题。所以一般不用

}

上述代码具体见《QThread》

1 #ifndef MYWIDGET_H 2 #define MYWIDGET_H 3  4 #include 
5 #include
//定时器头文件 6 #include "mythread.h" //线程头文件 7 8 namespace Ui { 9 class MyWidget;10 }11 12 class MyWidget : public QWidget13 {14 Q_OBJECT15 16 public:17 explicit MyWidget(QWidget *parent = 0);18 ~MyWidget();19 20 void dealTimeout(); //定时器槽函数21 void dealDone(); //线程结束槽函数22 void stopThread(); //停止线程槽函数23 24 private slots:25 void on_pushButton_clicked();26 27 private:28 Ui::MyWidget *ui;29 30 QTimer *myTimer; //声明变量31 MyThread *thread; //线程对象32 };33 34 #endif // MYWIDGET_H
mywidget.h
1 #include "mywidget.h" 2 #include "ui_mywidget.h" 3 #include 
4 #include
5 6 MyWidget::MyWidget(QWidget *parent) : 7 QWidget(parent), 8 ui(new Ui::MyWidget) 9 {10 ui->setupUi(this);11 12 myTimer = new QTimer(this);13 14 //只要定时器启动,自动触发timeout()15 connect(myTimer, &QTimer::timeout, this, &MyWidget::dealTimeout);16 17 //分配空间18 thread = new MyThread(this);19 20 21 connect(thread, &MyThread::isDone, this, &MyWidget::dealDone);22 23 //当按窗口右上角x时,窗口触发destroyed()24 connect(this, &MyWidget::destroyed, this, &MyWidget::stopThread);25 26 }27 28 void MyWidget::stopThread()29 {30 //停止线程31 thread->quit();32 //等待线程处理完手头动作33 thread->wait();34 }35 36 void MyWidget::dealDone()37 {38 qDebug() << "it is over";39 myTimer->stop(); //关闭定时器40 }41 42 void MyWidget::dealTimeout()43 {44 static int i = 0;45 i++;46 //设置lcd的值47 ui->lcdNumber->display(i);48 }49 50 MyWidget::~MyWidget()51 {52 delete ui;53 }54 55 void MyWidget::on_pushButton_clicked()56 {57 //如果定时器没有工作58 if(myTimer->isActive() == false)59 {60 myTimer->start(100);61 }62 63 //启动线程,处理数据64 thread->start();65 66 }
mywidget.cpp
1 #ifndef MYTHREAD_H 2 #define MYTHREAD_H 3  4 #include 
5 6 7 class MyThread : public QThread 8 { 9 Q_OBJECT10 public:11 explicit MyThread(QObject *parent = 0);12 13 protected:14 //QThread的虚函数15 //线程处理函数16 //不能直接调用,通过start()间接调用17 void run();18 19 signals:20 void isDone();21 22 public slots:23 };24 25 #endif // MYTHREAD_H
mythread.h
1 #include "mythread.h" 2  3 MyThread::MyThread(QObject *parent) : QThread(parent) 4 { 5  6 } 7  8 void MyThread::run() 9 {10     //很复杂的数据处理11     //需要耗时5s12     sleep(5);13 14     emit isDone();15 }
mythread.cpp

 

3.Qt4.7以后 线程的使用方法:

1).定义:

(1).设定一个类,继承与QObject 

(2).类中设置一个线程函数(只有一个是线程函数,函数名可以任意)

class MyThread : public QObject

{

public:

  void fun();//

}

 

2).使用:

(1).创建自定义线程对象(不能指定父对象),

否则后续就无法加入到QThread线程对象,因为指定后是已经给了窗口了就不能放到线程的地方了

或者说已经有父对象的则不能再移动了(不能移动到别的父对象)

 

myThread=new MyThread;

 

(2).创建QThread线程对象

QThread *thread = new QThread(this)

(3).把自定义线程类,加入到QThread线程对象,  关联起来

myThread->moveToThread(thread);

 

(4).启动线程对象的线程,

thread.start();//只是把线程开启了,并没有启动线程处理函数(线程是启动了,但是线程函数没有启动)

 

(5).线程处理函数(自定义线程函数)的启动,必须通过signal-slot 信号和槽的方式(不能直接调用这个函数):

主线程发送信号,并且信号的处理函数指定为线程处理函数,这样线程处理函数才启动了

(定义信号与槽必须要有Q_OBJECT宏)

connect(this, &MyWidget::startThread, myT, &MyThread::myTimeout);

 

(6).线程退出,直接退出是不行的,因为线程处理函数中的while(1)这样退出不了

//当按窗口右上角x时,窗口触发destroyed(),如果此时不关闭线程,则关了窗口线程还会运行

 connect(this, &MyWidget::destroyed, this, &MyWidget::dealClose);//不像linux中进程关了则线程都关了,这里线程还会运行

 

注意使用前面:

    thread->quit();//等待线程处理完,但是线程是死循环一致处理不完,所以线程无法退出

    thread->wait();

这样线程不退出,必须先让线程处理函数退出循环,通过标志位的方法:

         myT->setFlag(true);//设置退出标记,则循环条件不满足,则线程处理函数会退出

    thread->quit();

    thread->wait()

 

3).返回处理结果,

可以在类中定义一个信号,信号可以是带参数的,参数可以是处理后的数据

然后在需要(处理好后)的时候发送这个信号,来告诉其他线程。

        

具体见图《线程2》

 

4.实现:

void MyThread::myTimeout()

{

    while( !isStop )

    {

 

        QThread::sleep(1);

        emit mySignal();

        //QMessageBox::aboutQt(NULL);

 

         qDebug() << "子线程号:" << QThread::currentThread();

 

         if(isStop)

         {

             break;

         }

    }

}

void MyWidget::on_buttonStart_clicked()

{

    if(thread->isRunning() == true)

    {

        return;

    }

 

    //启动线程,但是没有启动线程处理函数

    thread->start();

    myT->setFlag(false);

 

    //不能直接调用线程处理函数,

    //直接调用,导致,线程处理函数和主线程是在同一个线程

    //myT->myTimeout();

 

    //只能通过 signal - slot 方式调用

    emit startThread();

}

void MyWidget::on_buttonStop_clicked()

{

    if(thread->isRunning() == false)

    {

        return;

    }

    myT->setFlag(true);

    thread->quit();

    thread->wait();

}

 

5.注意:

1).线程处理函数内部,不允许操作图形界面,也不允许创建图形界面,否则程序会奔溃。

线程处理函数内部一般是纯数据处理

 

2).connect()第五个参数的作用,第五个参数多线程时才有意义

connect()第五个参数表示的是连接方式,主要有三种:自动连接,队列连接,直接连接

默认的时候,用的是自动连接,自动连接时:

    如果是多线程,默认使用队列连接 (通过(接收的)类来判断是否是子线程的)

    如果是单线程, 默认使用直接连接方式

        

队列连接含义: 槽函数所在的线程和接收者一样

connect(this, &MyWidget::startThread, myT, &MyThread::myTimeout);

//槽函数所在的线程和myT一样,即在子线程中

 

直接连接含义:槽函数所在线程和发送者一样

connect(this, &MyWidget::startThread, myT, &MyThread::myTimeout,Qt::DirectConnection);

//槽函数所在的线程和this一样,即在主线程中,无法实现多任务

 

这就是为啥,启动线程处理函数使用connect的原因,默认是队列方式(多线程下)。

 

一般用默认就够了

 

上述代码具体见《ThreadPro》

1 #ifndef MYWIDGET_H 2 #define MYWIDGET_H 3  4 #include 
5 #include "mythread.h" 6 #include
7 8 namespace Ui { 9 class MyWidget;10 }11 12 class MyWidget : public QWidget13 {14 Q_OBJECT15 16 public:17 explicit MyWidget(QWidget *parent = 0);18 ~MyWidget();19 20 void dealSignal();21 void dealClose();22 23 signals:24 void startThread(); //启动子线程的信号25 26 private slots:27 void on_buttonStart_clicked();28 29 void on_buttonStop_clicked();30 31 private:32 Ui::MyWidget *ui;33 MyThread *myT;34 QThread *thread;35 36 };37 38 #endif // MYWIDGET_H
mywidget.h
1 #include "mywidget.h" 2 #include "ui_mywidget.h" 3 #include 
4 5 6 MyWidget::MyWidget(QWidget *parent) : 7 QWidget(parent), 8 ui(new Ui::MyWidget) 9 {10 ui->setupUi(this);11 12 //动态分配空间,不能指定父对象13 myT = new MyThread;14 15 //创建子线程16 thread = new QThread(this);17 18 //把自定义线程加入到子线程中19 myT->moveToThread(thread);20 21 connect(myT, &MyThread::mySignal, this, &MyWidget::dealSignal);22 23 qDebug() << "主线程号:" << QThread::currentThread();24 25 connect(this, &MyWidget::startThread, myT, &MyThread::myTimeout);26 27 28 connect(this, &MyWidget::destroyed, this, &MyWidget::dealClose);29 30 //线程处理函数内部,不允许操作图形界面31 32 33 //connect()第五个参数的作用,连接方式:默认,队列,直接34 //多线程时才有意义35 //默认的时候36 //如果是多线程,默认使用队列37 //如果是单线程, 默认使用直接方式38 //队列: 槽函数所在的线程和接收者一样39 //直接:槽函数所在线程和发送者一样40 41 42 }43 44 MyWidget::~MyWidget()45 {46 delete ui;47 }48 49 void MyWidget::dealClose()50 {51 on_buttonStop_clicked();52 delete myT;53 }54 55 void MyWidget::dealSignal()56 {57 static int i = 0;58 i++;59 ui->lcdNumber->display(i);60 }61 62 void MyWidget::on_buttonStart_clicked()63 {64 65 if(thread->isRunning() == true)66 {67 return;68 }69 70 //启动线程,但是没有启动线程处理函数71 thread->start();72 myT->setFlag(false);73 74 //不能直接调用线程处理函数,75 //直接调用,导致,线程处理函数和主线程是在同一个线程76 //myT->myTimeout();77 78 //只能通过 signal - slot 方式调用79 emit startThread();80 81 82 }83 84 void MyWidget::on_buttonStop_clicked()85 {86 if(thread->isRunning() == false)87 {88 return;89 }90 91 myT->setFlag(true);92 thread->quit();93 thread->wait();94 }
mywidget.cpp
1 #ifndef MYTHREAD_H 2 #define MYTHREAD_H 3  4 #include 
5 6 class MyThread : public QObject 7 { 8 Q_OBJECT 9 public:10 explicit MyThread(QObject *parent = 0);11 12 //线程处理函数13 void myTimeout();14 15 void setFlag(bool flag = true);16 17 signals:18 void mySignal();19 20 public slots:21 22 private:23 bool isStop;24 };25 26 #endif // MYTHREAD_H
mythread.h
1 #include "mythread.h" 2 #include 
3 #include
4 #include
5 6 MyThread::MyThread(QObject *parent) : QObject(parent) 7 { 8 isStop = false; 9 }10 11 void MyThread::myTimeout()12 {13 while( !isStop )14 {15 16 QThread::sleep(1);17 emit mySignal();18 //QMessageBox::aboutQt(NULL);19 20 qDebug() << "子线程号:" << QThread::currentThread();21 22 if(isStop)23 {24 break;25 }26 }27 }28 29 void MyThread::setFlag(bool flag)30 {31 isStop = flag;32 }
mythread.cpp

 

两种使用线程的方法,新方式是推荐的,但是老方式更好用。

/*******************************************************************************************/

三、线程画图

线程中可以绘图,可以使用绘图设备QImage

绘画完毕后可以把绘图结果即绘图设备,通过带参数(即绘图设备)的信号发送给主窗口

 

当绘图很复杂时,当然是放在线程中最合适。

 

void MyThread::drawImage()

{

    //定义QImage绘图设备

    QImage image(500, 500, QImage::Format_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);

 

    //定义5个点

    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);

}

 

上述代码具体见《ThreadIamge》

1 #ifndef WIDGET_H 2 #define WIDGET_H 3  4 #include 
5 #include "mythread.h" 6 #include
7 #include
8 9 namespace Ui {10 class Widget;11 }12 13 class Widget : public QWidget14 {15 Q_OBJECT16 17 public:18 explicit Widget(QWidget *parent = 0);19 ~Widget();20 21 //重写绘图事件22 void paintEvent(QPaintEvent *);23 24 void getImage(QImage); //槽函数25 void dealClose(); //窗口关闭槽函数26 27 private:28 Ui::Widget *ui;29 QImage image;30 MyThread *myT; //自定义线程对象31 QThread *thread; //子线程32 };33 34 #endif // WIDGET_H
widget.h
1 #include "widget.h" 2 #include "ui_widget.h" 3 #include 
4 #include
5 6 Widget::Widget(QWidget *parent) : 7 QWidget(parent), 8 ui(new Ui::Widget) 9 {10 ui->setupUi(this);11 12 //自定义类对象,分配空间,不可以指定父对象13 myT = new MyThread;14 15 //创建子线程16 thread = new QThread(this);17 18 //把自定义模块添加到子线程19 myT->moveToThread(thread);20 21 //启动子线程,但是,并没有启动线程处理函数22 thread->start();23 24 //线程处理函数,必须通过signal - slot 调用25 connect(ui->pushButton, &QPushButton::pressed, myT, &MyThread::drawImage);26 connect(myT, &MyThread::updateImage, this, &Widget::getImage);27 28 connect(this, &Widget::destroyed, this, &Widget::dealClose);29 30 }31 32 Widget::~Widget()33 {34 delete ui;35 }36 37 void Widget::dealClose()38 {39 //退出子线程40 thread->quit();41 //回收资源42 thread->wait();43 delete myT;44 45 }46 47 void Widget::getImage(QImage temp)48 {49 image = temp;50 update(); //更新窗口,间接调用paintEvent()51 }52 53 void Widget::paintEvent(QPaintEvent *)54 {55 QPainter p(this); //创建画家,指定绘图设备为窗口56 p.drawImage(50, 50, image);57 }
widget.cpp
1 #ifndef MYTHREAD_H 2 #define MYTHREAD_H 3  4 #include 
5 #include
6 7 class MyThread : public QObject 8 { 9 Q_OBJECT10 public:11 explicit MyThread(QObject *parent = 0);12 13 //线程处理函数14 void drawImage();15 16 signals:17 void updateImage(QImage temp);18 19 public slots:20 };21 22 #endif // MYTHREAD_H
mythread.h
1 #include "mythread.h" 2 #include 
3 #include
4 #include
5 #include
6 7 MyThread::MyThread(QObject *parent) : QObject(parent) 8 { 9 10 }11 12 void MyThread::drawImage()13 {14 //定义QImage绘图设备15 QImage image(500, 500, QImage::Format_ARGB32);16 //定义画家,指定绘图设备17 QPainter p(&image);18 19 20 //定义画笔对象21 QPen pen;22 pen.setWidth(5); //设置宽度23 //把画笔交给画家24 p.setPen(pen);25 26 //定义画刷27 QBrush brush;28 brush.setStyle(Qt::SolidPattern); //设置样式29 brush.setColor(Qt::red); //设置颜色30 //把画刷交给画家31 p.setBrush(brush);32 33 //定义5个点34 QPoint a[] =35 {36 QPoint(qrand()%500, qrand()%500),37 QPoint(qrand()%500, qrand()%500),38 QPoint(qrand()%500, qrand()%500),39 QPoint(qrand()%500, qrand()%500),40 QPoint(qrand()%500, qrand()%500)41 };42 43 p.drawPolygon(a, 5);44 45 46 //通过信号发送图片47 emit updateImage(image);48 49 }
mythread.cpp

 

转载于:https://www.cnblogs.com/yuweifeng/p/9384728.html

你可能感兴趣的文章
如何提升爬虫的性能
查看>>
回文树/回文自动机(PAM)学习笔记
查看>>
选择器
查看>>
String类的subtring(,)
查看>>
Android 特别大的Activity和Fragment的生命周期图
查看>>
让你的eclipse实现写JAVA代码,HTML,CSS,JAVASCRIPT代码提示
查看>>
Winform dataGridview 为每一个单元格制定一个tooptip
查看>>
BZOJ.2938.[POI2000]病毒(AC自动机)
查看>>
4 —— node —— 启动一个 http 服务器
查看>>
VOIP RTP RTSP 实现 Baresip 源码分析
查看>>
[非原创] 常用加密算法整理 AES/SSL(一)
查看>>
Spring 专题 文章(转)
查看>>
301页面转向 php
查看>>
生成器和迭代器的区别
查看>>
gp 服务的发布与javascript调用
查看>>
CF336B[思维题]
查看>>
php文本操作方法集合比较第2页
查看>>
kafka安装教程
查看>>
PE文件结构解析
查看>>
ubuntu下安装fcitx小企鹅输入法
查看>>