qt窗口应用程序案例
code-1 |
#include <iostream> #include <QApplication> #include <QtWidgets/QWidget>
using namespace std;
class MyClass : public QWidget { // Q_OBJECT
public: MyClass(QWidget *parent = 0):QWidget(parent){} ~MyClass(){} void doThings() { std::cout<<"ggg"<<std::endl; } private: bool event(QEvent* ev) { if (ev->type() == QEvent::PolishRequest) { // overwrite handling of PolishRequest if any doThings(); return true; } else if (ev->type() == QEvent::Show) { // complement handling of Show if any doThings(); QWidget::event(ev); return true; } // Make sure the rest of events are handled return QWidget::event(ev); } };
int main(int argc, char *argv[]) { QApplication a(argc, argv); MyClass c; c.show(); return a.exec(); } |
代码中,首先派生Widget类,并复写了event()方法,用户自己处理事件。为了能够运行窗口应用程序,在main函数中,首先需要定义一个QApplication实例,然后实例化窗口对象,最后启动QApplication::exec()进入事件循环(文档中对exec函数的描述)。qt框架是事件驱动的应用程序框架,为了探究其事件模型,我们对两个对象QApplication和QWidget进行研究。
首先,我们知道,qt窗口对象最底层实际上是对各平台提供的窗口API的封装,从而实现跨平台。在win平台中,一个窗口对象对应一个线程(win窗口基于消息驱动),并且win平台也提供从线程的消息堆栈获取消息的API,所以猜测qt在win平台上的窗口线程,会不断获取系统窗口事件,然后以某种方式去处理这些事件。
至于如何处理这些事件,就就涉及另一概念,事件模型中的事件中心,以通常事件驱动模型的实现经验上看,事件中心应该由专门的Daemon线程在管理,那么这个线程在何处被创建呢?观察main函数后,不难发现,除了MyClass对象,只有QApplication对象有可能。通过阅读文档,印证了猜想。在一个qt应用程序中,创建QApplication的线程是主线程,在此主线中,调用QApplication::exec()进入事件循环,不断处理产生的事件(消息)。因此,我们可以对qt的事件模驱动模型有一个大致的认识:
- 程序中存在一个事件(消息)总线 event bus
- qt应用程序的事件来自用户对窗口的操作,窗口线程在获取这些事件后,调用postEvent()将event对象投递到event bus中
- event bus由Daemon线程管理,线程的事件循环核心工作便是处理这些事件
事件模型
更深入的了解qt事件驱动框架后,其简化的模型如下:
图1 qt事件驱动模型
其中,Win thread不断调用GetMessage()从win窗口线程的消息堆栈获取消息,并转化为qt消息Event对象,投递到Event Bus中。而Daemon thread则不断从Event Bus中获取Event,然后遍历事件的观察者,逐个调用其event()方法。若观察者成功处理事件,则event返回true,否则返回false。
经过调试跟踪代码发现,qt窗口事件的处理不是异步而是同步方式处理,即事件句柄的调用由Daemon thread调用。采用这种方式的原因,猜测可能的原因:
1.qt的开发者希望事件的产生和处理分离,窗口线程专注于收集/响应用户交互事件与界面绘制,事件处理则由专门线程集中处理,使得界面线程不易因为处理事件而陷入阻塞状态,满足界面响应的实时性,从而提供更好的用户交互体验;
2.用户在使用抓面应用程序时,不大可能同时激活多个窗口且同时进行操作,因此窗口事件的并发量不大,集中式的处理方式利于管理。