事件循环
事件循环是用于处理和调度应用程序中各种异步事件的机制,负责接收和分发事件队列中的各种事件,当GUI程序的事件循环开启后,它便一直在循环处理事件队列中的事件,直到满足条件退出循环,便不再处理事件队列中的事件。
Qt事件循环
Qt的事件循环是Qt作为gui框架的核心,它提供一系列事件处理逻辑,使用户可以通过安装事件过滤器、重写各种event函数,让每个组件对各种封装好的事件进行自定义处理。比起原生窗口过程响应速度,Qt的事件要慢一些,但它给开发人员提供了灵活便捷的事件处理方法。
除了便于事件处理,还可以从另一个角度看待事件循环。熟悉QWidget就会知道,QWidget对象在主线程外的线程操作很容易导致崩溃。而每一个可见的QWidget都会注册一个qWindowsWndProc窗口过程函数,用来处理系统消息,当用户操作鼠标键盘产生消息时。qWindowsWndProc回调并封装该消息成为QEvent,将封装的QEvent添加到事件循环处理的事件队列,再由事件循环去分发事件。
乍一看这种方式是绕了一圈,还增加了事件处理延迟,但背后有两个原因,至少目前笔者是这个认为:1、不能直接在qWindowsWndProc封装好QEvent后直接处理,是因为在非主线程操作可能引起崩溃,QWidget是线程不安全的。2、事件循环是Qt的核心,除了gui还会处理socket、timer事件,因此将QEvent放进事件队列,再让事件循环处理,本质上是一种解耦。
如果上面的话对你有些许帮助,后面的可能不用看了,因为下面主要是笔者记录的一系列调用过程,主要从事件具体在什么地方产生、什么地方封装、什么地方处理进行的记录。
Qt事件循环调用过程
Qt源码版本:5.15.2
事件循环通过QGuiApplication::exec()启动,最终调用到QEventLoop::exec,该函数以while循环调用QEventLoop::processEvents,结束条件是QEventLoopPrivate.exit.loadAcquire()。最终QEventLoopPrivate.threadData.eventDispatcher.processEvents() 进行事件处理。
int QGuiApplication::exec()
{
#ifndef QT_NO_ACCESSIBILITY
QAccessible::setRootObject(qApp);
#endif
return QCoreApplication::exec();
}
↓
int QCoreApplication::exec()
{
//*********************************省略代码
QEventLoop eventLoop;
int returnCode = eventLoop.exec();
//*********************************省略代码
}
↓
int QEventLoop::exec(ProcessEventsFlags flags)
{
//*********************************省略代码
while (!d->exit.loadAcquire())
processEvents(flags | WaitForMoreEvents | EventLoopExec);
//*********************************省略代码
}
↓
bool QEventLoop::processEvents(ProcessEventsFlags flags)
{
Q_D(QEventLoop);
auto threadData = d->threadData.loadRelaxed();
if (!threadData->hasEventDispatcher())
return false;
return threadData->eventDispatcher.loadRelaxed()->processEvents(flags);
}//至此事件循环启动完毕,threadData中的eventDispatcher在windows平台下是QWindowsGuiEventDispatcher类
窗口函数注册(消息的来源):
qt_internal_proc //每个Application只有一个该窗口处理函数,通过该窗口过程函数,处理发给该窗口的 WM_QT_SOCKETNOTIFIER、WM_QT_ACTIVATENOTIFIERS、WM_TIMER、WM_QT_SENDPOSTEDEVENTS 类型消息。
qt_internal_proc处理过程:
LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp)
{
//*********************************省略代码
switch (message) {
case WM_QT_SOCKETNOTIFIER: {
//*********************************省略代码
}
case WM_QT_ACTIVATENOTIFIERS: {
//*********************************省略代码
}
case WM_TIMER:
//*********************************省略代码
case WM_QT_SENDPOSTEDEVENTS:
//*********************************省略代码
q->sendPostedEvents();
}
//*********************************省略代码
}
参考注册调用栈:
qt_internal_proc qeventdispatcher_win.cpp
qt_create_internal_window qeventdispatcher_win.cpp
QEventDispatcherWin32::createInt