概述
本文仅浅浅的介绍些许QApplication相关的事件循环。
详情
Qt是以事件为驱动的。接下来浅谈一下。
线程相关性
当我们创建一个QObject时,它会与创建自己所在的线程绑定。它参与的消息循环,其实是它所在线程的消息循环,如上图所示。假如某个线程没有默认的QThread::exec(),那么该线程上的QObject则无法接收到事件。另外,如果两个不同线程的QObject需要相互通信,那么只能通过QueuedConnection的方式,异步通知对方线程,在下一轮消息循环处理QObject的消息。
QObject的线程相关性默认会和它的parent保持一致。如果一个QObject没有parent,那么可以通过moveToThread,将它的线程相关性切换到指定线程。
Qt事件循环(以Windows为例)
Qt事件循环是基于Windows消息处理。Qt将系统消息(软件中断,像窗体的鼠标、键盘、输入法、绘制,各种消息)转换成Qt事件,并且将事件封装成类,所有的事件类都是由QEvent派生的,事件的产生和处理就是Qt程序的主轴,且伴随整个程序的运行周期。
当程序启动之后,进入main函数,main函数中会有一个QApplication(QCoreApplication)对象,该对象会调用exec,此时当前程序的事件循环被启动,之后QApplication(QCoreApplication)不断的从系统获取消息,这些消息转换为事件,存入事件队列,事件循环没有退出之前,会不断从队列中取事件,然后进行事件的分发,到指定的类对象的具体事件进行事件处理。
在Qt中,消息循环在QEventLoop类中实现。通过QEventLoop::exec()可以进入一个消息循环的阻塞状态中,也就是不断地PeekMessage-DispatchMessage进行消息的获取和分发。
以示例的方式展示运行流程
本文采用一个常用的例子为示例。
示例
本文开始的一个示例,是以创建QApplication应用程序为例,基于QDialog的应用程序。这里仅展示main.cpp中的代码,用以说明QApplication的事件循环机制。
main.cpp
#include "dialog.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Dialog w;
w.show();
return a.exec();
}
其中a.exec()为启动事件循环。安装Qt源码,并且下载对应编译器的调试文件之后,可以在a.exec()这行打断点调试,F11进入到源码中可以看到:
QApplication调用了QGuiApplication的事件循环exec(),而在QGuiApplication中:
又调用QCoreApplication的事件循环exec(),其函数的实现如下:
先是判断当前线程是不是主线程,因为mian函数中的exec()事件循环是在主线程中启动的,且当前主线程的事件循环中还没有任何需要处理的事件,所以为空,接下来创建了QEventloop对象,并调用了exec(),启动了QEventloop的事件循环。此时要是没有相应的事件发生,如鼠标,键盘之类的事件,断点便在此exec处停滞。直到有事件发生。
QEventLoop的事件循环函数源码如下:
这里默认是处理所有的事件。真正的处理工作是在ProcessEvents(flags|…),中进行的,当没有事件的时候,会一直阻塞等待事件到来。其函数processEvent内部实现如下:
若没有事件分发,则while循环为一个空循环,若有事件调度,则调用processEvent处理事件。
QEventDispatcherWin32此类负责消息的获取和分发。
其本质上就是等待消息到来,然后将系统消息转换为Qt事件,进行事件分发,到具体的窗口类中进行事件处理。
事件循环在工作中的使用
参考文章
https://zhuanlan.zhihu.com/p/113695485