为了解决这个问题,我们必须得用线程来做。将大工作量的活交给次线程去作,主线程(app.exe())不会阻塞继续作界面上的工作。
举个例子:BackgroundWorker是次线程类,用于做一个复杂的运行(我们假设它很复杂)
backgroundworker.h
#ifndef BACKGROUNDWORKER_H #define BACKGROUNDWORKER_H #include <QThread> class BackgroundWorker : public QObject { Q_OBJECT public: BackgroundWorker(); ~BackgroundWorker(); signals: void updateResultSignal(double area); public slots: void calculateSlot(double radio); private: QThread thread; }; #endif // BACKGROUNDWORKER_Hbackgroundworker.cpp:
#include "backgroundworker.h" #include <qmath.h> BackgroundWorker::BackgroundWorker() { moveToThread(&thread); thread.start(); // TODO other initialize } BackgroundWorker::~BackgroundWorker() { thread.quit(); thread.wait(); // wait the thread quit normally } void BackgroundWorker::calculateSlot(double radio) { double area = 0.0; area = qPow(radio, 2) * M_PI; sleep(2); // OH! assume that it is a big work cost 2 seconds to finish it. emit updateResultSignal(area); }注意看构造函数哦!moveToThread(&thread)就是将this对象的信号与槽机制同主线程转交给thread线程去处理。然后就thread.start()启动线程。
BackgroundWorker::calculateSlot()为计算圆面积的函数。我们假设它的计算量很大,需要花上2秒钟才能完成。主线程在接受到周户的操作时,发送信号给BackgroundWorker,发送信号后主线程就继续处理其它事件去了。由于主线程与BackgroundWorker不是同一个线程,那么消息则是通过消息列队传到BackgroundWorker的,当处理这个BackgroundWorker消息循环的thread线程接收到消息队列里的消息后,会调用calculateSlot()槽进行计算。在完成计算后,再通过发送updateResultSignal()信号将结果返回给主线程,同样是通过消息队列的方式。
其它需要说明的:
(1)如果不同线程间信号发送中的参数有自定义的数据类型,那么就必须先注册到Qt内部的类型管理器中后才能在connect()中使用。
qRegisterMetaType<MyType>("MyType");(2)在次线程中,要不处理GUI相关的内容,包括弹出消息对话框。因为主线程主要负责GUI图像处理操作。如果线程也要插一手,那么可能就会引起冲突了。这样很不安全。
(4)不一定非要在类中包含一个QThread,也可以在外面定义一个QThread对象,并把多个对象moveToThread()到同一个线程去处理。
(5)默认情况下,一个对象是由哪个线程实例化的,那么它的消息机制就是由哪个线程来处理。
写一个ExecutableObject类,让所有继承该类的派生类都有独立的消息处理线程。
executableobject.h文件:
#ifndef EXECUTABLEOBJECT_H #define EXECUTABLEOBJECT_H #include <QObject> #include <QThread> class ExecutableObject : public QObject { Q_OBJECT public: explicit ExecutableObject(QObject *parent = 0); ~ExecutableObject(); private: QThread thread; }; #endif // EXECUTABLEOBJECT_Hexecutableobject.cpp文件:
#include "executableobject.h" ExecutableObject::ExecutableObject(QObject *parent) : QObject(parent) { moveToThread(&thread); thread.start(); } ExecutableObject::~ExecutableObject() { thread.quit(); thread.wait(); }
前面的BackgroundWorker可以精简成:
#ifndef BACKGROUNDWORKER_H #define BACKGROUNDWORKER_H #include <QThread> #include "executableobject.h" class BackgroundWorker : public ExecutableObject { Q_OBJECT public: BackgroundWorker() {} signals: void updateResultSignal(double area); public slots: void calculateSlot(double radio); }; #endif // BACKGROUNDWORKER_H
#include "backgroundworker.h" #include <qmath.h> void BackgroundWorker::calculateSlot(double radio) { double area = 0.0; area = qPow(radio, 2) * M_PI; sleep(2); // OH! assume that it is a big work cost 2 seconds to finish it. emit updateResultSignal(area); }