QT线程发送消息通知界面小例

初学QT,有很多地方都不懂,靠着Win32开发的样子写程序到是出了不少问题,摸索中前进。不管是什么开发,都有一条基本的原则:不要在UI线程中进行耗时操作,这样会导致界面卡顿;不要在辅助线程中操作UI界面,这样会导致界面刷新不及时。对于基本的Windows程序,都少不了消息循环和往消息队列中发送消息的函数(SendMessage PostMessage)。查看基类头文件,可以看到详细的接口,一般虚函数都是说明我们自己可以去实现并处理的。
QMainWindow的消息处理函数:virtual bool event(QEvent *event);
那么QEvent就是我们要发送的消息了,继续F2定位过去,看到里面有个消息ID的枚举,是不是和Win32开发的Windows自定义消息很像(Windows内核都是C写的,消息ID当然都是C原因的宏定义实现。QT是C++写的,枚举更加方便有木有)。注意查看这些ID的值

User = 1000,// first user event id MaxUser = 65535// last user event id };


也即是说我们自定义消息的ID都要从User开始 ,以免与QT内部消息重合,这就和Win32里的自定义消息一般都是从WM_USER开始一样的道理。 【QT线程发送消息通知界面小例】继续查看资料,发送消息有两种方式sendEvent 和postEvent


1、 QCoreApplication :: sendEvent (); 根据Qt Asistant 里面的讲述,这个函数直接将事件消息直接发送给接受者进行处理,等到事件处理完毕后才返回;并且使用它所传递的消息事件是在 栈(stack) 上创建的,也就是说它的内存空间是有编译器来自动管理的。2、 QCoreApplication :: postEvent (); 根据Qt Asistant 里面的讲述, 使用这个函数来传递时间消息时,它将事件消息发送到接受者的的消息队列里面,然后立即返回,不需要等到事件处理完毕才返回;并且使用它所传递的消息事件是在 堆(heep) 上创建的,也就是说它的内存空间是又程序员自己管理的,如用 new 创建的变量!

QEvent类的数据有限,如果消息需要加上很多自己的数据,我们可以派生QEvent类,在成员变量里面另外加上数据。这里我需要加上一个数值来设置进度条的进度,所以派生了下,提供接口设置和获取进度值。
#ifndef MYEVENT #define MYEVENTclass MyEvent : public QEvent { public: MyEvent(QEvent::Type type) : QEvent(type) {} ~MyEvent() {}void SetValue(int nValue) { m_nValue = https://www.it610.com/article/nValue; }int GetValue() { return m_nValue; }private: int m_nValue; }; #endif // MYEVENT


开启线程,不断发送消息设置进度。 刚开始使用的是 sendEvent,线程函数如下:
DWORD MainWindow::Thread1(void *lpParam) { MainWindow* pWnd = (MainWindow*)lpParam; MyEvent me(QEvent::Type(QEvent::User+1)); for ( int i=0; i<=100; ++i ) { me.SetValue(i); //QApplication::postEvent(pWnd, new MyEvent(me)); QEvent evt(me); QApplication::sendEvent(pWnd, &evt); if ( i == 100 ) i = 0; Sleep(10); } return 0; }


但是运行时却弹出了一个错误 QT线程发送消息通知界面小例
文章图片


翻一下就是:不能发送消息到另一个线程所属的对象!好吧,我以为这个sendEvent和Win32里面的SendMessage一样呢,发送消息到窗口线程的消息循环,等待消息处理完毕后才返回。实践证明,不是那样的。
最后,全部换成postEvent,并且QEvent对象都是通过new出来的。在消息循环中,我本来是用delete来释放这些申请的指针的,结果也是导致了崩溃了。查看调用堆栈,可以看到指针已经被QT内部机制析构了。代码通过开启两个线程,往消息循环中发送两个消息通知界面上的两个进度条不断改变进度。

bool MainWindow::event(QEvent *event) { switch (event->type()) { case QEvent::User+1: { MyEvent* pEvent = (MyEvent*)event; ui->progressBar->setValue(pEvent->GetValue()); //delete pEvent; return 0; } break; case QEvent::User+2: { MyEvent* pEvent = (MyEvent*)event; ui->progressBar_2->setValue(pEvent->GetValue()); //delete pEvent; return 0; } break; default: break; } return QMainWindow::event(event); }DWORD MainWindow::Thread1(void *lpParam) { MainWindow* pWnd = (MainWindow*)lpParam; MyEvent me(QEvent::Type(QEvent::User+1)); for ( int i=0; i<=100; ++i ) { me.SetValue(i); QApplication::postEvent(pWnd, new MyEvent(me)); if ( i == 100 ) i = 0; Sleep(10); } return 0; }UINT MainWindow::Thread2(void *lpParam) { MainWindow* pWnd = (MainWindow*)lpParam; MyEvent me(QEvent::Type(QEvent::User+2)); int i = 100; while( true ) { me.SetValue(i); QApplication::postEvent(pWnd, new MyEvent(me)); if ( i == 0 ) i = 100; Sleep(10); i--; } return 0; }



程序运行截图:
QT线程发送消息通知界面小例
文章图片


总结下
1、sendEvent只能用在同一个线程中;
2、postEvent发送出去的QEvent对象会自动析构,无需释放。










    推荐阅读