您的位置:首页 > 编程语言 > Qt开发

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

2016-03-21 22:43 232 查看
初学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开始一样的道理。

继续查看资料,发送消息有两种方式sendEvent 和postEvent

1、 QCoreApplication :: sendEvent ();   根据Qt Asistant 里面的讲述,这个函数直接将事件消息直接发送给接受者进行处理,等到事件处理完毕后才返回;并且使用它所传递的消息事件是在 栈(stack) 上创建的,也就是说它的内存空间是有编译器来自动管理的。

2、 QCoreApplication :: postEvent (); 根据Qt Asistant 里面的讲述, 使用这个函数来传递时间消息时,它将事件消息发送到接受者的的消息队列里面,然后立即返回,不需要等到事件处理完毕才返回;并且使用它所传递的消息事件是在 堆(heep) 上创建的,也就是说它的内存空间是又程序员自己管理的,如用 new 创建的变量!
QEvent类的数据有限,如果消息需要加上很多自己的数据,我们可以派生QEvent类,在成员变量里面另外加上数据。这里我需要加上一个数值来设置进度条的进度,所以派生了下,提供接口设置和获取进度值。
#ifndef MYEVENT
#define MYEVENT

class MyEvent
: public QEvent
{
public:
MyEvent(QEvent::Type type)
: QEvent(type)
{

}
~MyEvent()
{

}

void SetValue(int nValue)
{
m_nValue = 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对象会自动析构,无需释放。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: