在开发过程中,碰到一些耗时的操作,如果直接在主线程里处理,UI将会出现假死的现象,甚至程序会崩溃,所以这类操作最好放在子线程当中。
这里有2个对象,一个是QThread,即子线程;另一个是继承于QObject的一个类,这个是要在子线程里运行的代码片段。
因为这个QObject非常基础,所以几乎所有继承于其他组件的自定义类,都间接地继承于QObject,都可以放入QThread中运行。
1、首先定义一个要在子线程跑的自定义类
ThreadObject.h:
#ifndef THREADOBJECT_H #define THREADOBJECT_H #include <QPrinter> #include <QPainter> #include <mytablewidget.h> #include <QObject> class ThreadObject : public QObject { Q_OBJECT public: explicit ThreadObject(QObject *parent = 0); ~ThreadObject(); signals: void onRunCompletedSignal(); public slots: void runOutputPdf(QString path,MyTableWidget * table,QString currentProgressName,QString currentProgressVer); }; #endif // THREADOBJECT_H
ThreadObject.cpp:
#include "threadobject.h" ThreadObject::ThreadObject(QObject *parent) : QObject(parent) { } void ThreadObject::runOutputPdf(QString path,MyTableWidget * table,QString currentProgressName,QString currentProgressVer) { // 一些耗时的操作. emit onRunCompletedSignal(); } ThreadObject::~ThreadObject() { }
这个类没有很特别,只有两点需要注意:
1、在执行耗时操作完毕之后,要发送一个信号,这个信号将会在主线程被接收到;
2、定义一个public slot,这个回调将会有主线程的信号(signal)来触发,运行。
2、在主线程里调用
if(thread) return; // 实例化子线程 thread = new QThread; // 实例化子线程对象 threadObject = new ThreadObject; // 将子线程对象移到子线程 threadObject->moveToThread(thread); // 线程finished->线程自己deleteLater connect(thread,&QThread::finished,thread,&QThread::deleteLater); // 线程finished->线程对象deleteLater connect(thread,&QThread::finished,threadObject,&ThreadObject::deleteLater); // 主线程发送信号->线程对象启动run函数 connect(this,&ExtractProgressEditPage::startThreadObjectRunOutputPdfSignal,threadObject,&ThreadObject::runOutputPdf); // 接收线程实体运行结束发送的信号,在回调里quit,quit之后会接收到finished,然后再根据finished消息进一步操作 connect(threadObject,&ThreadObject::onRunCompletedSignal,this,&ExtractProgressEditPage::onOutputPdfCompleted); thread->start(); emit startThreadObjectRunOutputPdfSignal(path,table,currentProgressName,currentProgressVer);
3、子线程的销毁:注意,必须手动对子线程进行quit,并不是代码运行完了他就自动销毁!
如上所述,在耗时操作运行完毕之后,即我们可以结束线程之时,发射一个信号,这个信号在主线程被捕获,然后在回调里我们要对这个子线程进行注销:
void ExtractProgressEditPage::onOutputPdfCompleted() { qDebug()<<"pdf output completed!"; thread->quit(); thread->wait(); thread->deleteLater(); thread = NULL;// 加了这个是为了以上代码有的地方通过if(thread)来判断线程是否已运行结束,然后是否可以进行新的一个运行 }
在调用了quit之后,他自然会触发finished,然后触发其他我们如上面信号槽所定义的其他行为,比如对ThreadObject进行deleteLater。