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;
}
但是运行时却弹出了一个错误
文章图片
翻一下就是:不能发送消息到另一个线程所属的对象!好吧,我以为这个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;
}
程序运行截图:
文章图片
总结下
1、sendEvent只能用在同一个线程中;
2、postEvent发送出去的QEvent对象会自动析构,无需释放。
推荐阅读
- 危险也是机会
- python学习之|python学习之 实现QQ自动发送消息
- Linux下面如何查看tomcat已经使用多少线程
- 多线程NSOperation
- 深入浅出谈一下有关分布式消息技术(Kafka)
- 夏夜|夏夜 我们
- spring|spring boot中设置异步请求默认使用的线程池
- Vue组件之事件总线和消息发布订阅详解
- Android中非UI主线程能不能操作UI()
- Redis——发布订阅/消息队列