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开始一样的道理。

  继续查看资料,发送消息有两种方式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;
}

但是运行时却弹出了一个错误

QT线程发送消息通知界面小例_第1张图片

  翻一下就是:不能发送消息到另一个线程所属的对象!好吧,我以为这个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线程发送消息通知界面小例_第2张图片

总结下

1、sendEvent只能用在同一个线程中;

2、postEvent发送出去的QEvent对象会自动析构,无需释放。







你可能感兴趣的:(QT)