序
UI程序中经常有一些耗时的操作要处理,如果直接在UI线程中执行,整个界面都会卡住,出现无响应状态。
如果每次操作都生成一个线程类,而且相关数据都要传递过去,可能处理起来会比较麻烦!
因此本篇文章的初衷就是想简化这种情况。
仿照MFC方式解决
最开始我想到的就是按照MFC创建线程的方式进行解决。
通过类的 static 成员函数作为函数指针,通过QThread的run函数进行回调。
思路如下:
- UI 部分大致逻辑如下
class Form : public QWidget { Q_OBJECT public: Form(QWidget *parent=0) : QWidget(parent) { ... m_thread.setCallBack(run, this); m_thread.start(); } ... private: void static run(QObject* obj) { Form*frm = (Form*) obj; frm->todo(); } void todo() { // 添加需要做的事情 } ... private: CThread m_thread; }
- CThread实现方式如下
#ifndef CTHREAD_H #define CTHREAD_H #include <QThread> // 函数指针定义,用于回调的;QObject *即为传递过来的 Form 的 this 对象指针 typedef void (*pFuncPointer)(QObject *); class CThread : public QThread { public: CThread(QObject *obj=0):QThread(obj) {m_obj = obj;} CThread(pFuncPointer func, QObject *obj):QThread(obj) {m_func = func; m_obj = obj;} void setCallBack(pFuncPointer func, QObject *obj) {m_func = func; m_obj = obj;} void setObj(QObject *obj) {m_obj = obj;} void setFun(pFuncPointer func) {m_func = func;} private: void run(){m_func(m_obj);} // 回调函数,将 this 传递回去 private: pFuncPointer m_func; QObject *m_obj; }; #endif // CTHREAD_H
更进一步
由于我实在太懒了,觉得定义按照MFC那样的方式定义 static 成员函数 实在太麻烦了;
想直接一步到位,如果这样可以通过传递类的函数指针,这样就可以简化成下面这样!
思路如下:
- UI 部分大致逻辑如下
class Form : public QWidget { Q_OBJECT public: Form(QWidget *parent=0) : QWidget(parent) { ... m_thread.setCallBack(&MainForm::todo, this); // 成员函数指针 m_thread.start(); } ... private: void todo() { // 添加需要做的事情 } ... private: CThread<Form> m_thread; }
- CThread实现方式如下
#ifndef CTHREAD_H #define CTHREAD_H #include <QThread> template <typename T> // 模板 class CThread : public QThread { public: typedef void (T::*pFuncPointer)(); // 模板函数指针 CThread(T *obj=0):QThread(obj) {m_obj = obj;} CThread(pFuncPointer func, T *obj):QThread(obj) {m_func = func; m_obj = obj;} void setCallBack(pFuncPointer func, T *obj) {m_func = func; m_obj = obj;} void setObj(T *obj) {m_obj = obj;} void setFun(pFuncPointer func) {m_func = func;} private: void run(){(m_obj->*m_func)();} // 回调,这里的写法需要注意 private: pFuncPointer m_func; T *m_obj; }; #endif // CTHREAD_H
需要注意的地方
由于 todo 函数虽然是 Form 的成员函数,但是它是被另一个线程所调用的,所以它不属于UI线程。
在更新界面相关,或者需要更新界面信息时,可以在 Form 里面定义信号和对应的槽;
而且连接的时候,最好明确指明最后一个参数为Qt::QueuedConnection。
总结
我的砖就抛到这里,希望对你们有用。
接下来的路就请各位小伙伴们自己走了!