事件循环
事件循环是用于处理和调度应用程序中各种异步事件的机制,负责接收和分发事件队列中的各种事件,当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::createInternalHwnd qeventdispatcher_win.cpp
QEventDispatcherWin32::QEventDispatcherWin32 qeventdispatcher_win.cpp
QWindowsGuiEventDispatcher::QWindowsGuiEventDispatcher qwindowsguieventdispatcher.cpp
QWindowsIntegration::createEventDispatcher qwindowsintegration.cpp
QGuiApplicationPrivate::createEventDispatcher qguiapplication.cpp
QApplicationPrivate::createEventDispatcher qapplication.cpp
QCoreApplicationPrivate::init qcoreapplication.cpp
QGuiApplicationPrivate::init qguiapplication.cpp
QApplicationPrivate::init qapplication.cpp
QApplication::QApplication qapplication.cpp
qWindowsWndProc //每个QWidget都有该窗口处理函数,通过该窗口过程函数,封装事件
qWindowsWndProc 处理过程:
extern "C" LRESULT QT_WIN_CALLBACK qWindowsWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
//*********************************省略代码
const bool handled = QWindowsContext::instance()->windowsProc(hwnd, message, et, wParam, lParam, &result, &platformWindow);
//*********************************省略代码
}
参考注册调用栈:
qWindowsWndProc qwindowscontext.cpp
WindowCreationData::create qwindowswindow.cpp
QWindowsWindowData::create qwindowswindow.cpp
QWindowsIntegration::createPlatformWindow qwindowsintegration.cpp
QWindowPrivate::create qwindow.cpp
QWindow::create qwindow.cpp
事件的封装(事件的来源):
过程:
extern "C" LRESULT QT_WIN_CALLBACK qWindowsWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
//*********************************省略代码
const bool handled = QWindowsContext::instance()->windowsProc(hwnd, message, et, wParam, lParam, &result, &platformWindow);
//*********************************省略代码
}
↓
bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
QtWindows::WindowsEventType et,
WPARAM wParam, LPARAM lParam,
LRESULT *result,
QWindowsWindow **platformWindowPtr)
{
//*********************************省略代码
case QtWindows::MouseWheelEvent:
case QtWindows::MouseEvent:
case QtWindows::LeaveEvent:
{
QWindow *window = platformWindow->window();
while (window && (window->flags() & Qt::WindowTransparentForInput))
window = window->parent();
if (!window)
return false;
if (d->m_systemInfo & QWindowsContext::SI_SupportsPointer)
return sessionManagerInteractionBlocked() || d->m_pointerHandler.translateMouseEvent(window, hwnd, et, msg, result);
else
return sessionManagerInteractionBlocked() || d->m_mouseHandler.translateMouseEvent(window, hwnd, et, msg, result);
}
break;
//*********************************省略代码
}
↓
bool QWindowsPointerHandler::translateMouseEvent(QWindow *window,
HWND hwnd,
QtWindows::WindowsEventType et,
MSG msg,
LRESULT *result)
{
//*********************************省略代码
if (et == QtWindows::MouseWheelEvent)
return translateMouseWheelEvent(window, currentWindowUnderPointer, msg, globalPos, keyModifiers);
//*********************************省略代码
}
↓
bool QWindowsPointerHandler::translateMouseWheelEvent(QWindow *window,
QWindow *currentWindowUnderPointer,
MSG msg,
QPoint globalPos,
Qt::KeyboardModifiers keyModifiers)
{
//*********************************省略代码
QWindowSystemInterface::handleWheelEvent(receiver, localPos, globalPos, QPoint(), angleDelta, keyModifiers);
//*********************************省略代码
}
↓
//创建/封装事件
bool QWindowSystemInterface::handleWheelEvent(QWindow *window, ulong timestamp, const QPointF &local, const QPointF &global, QPoint pixelDelta, QPoint angleDelta, Qt::KeyboardModifiers mods, Qt::ScrollPhase phase,
Qt::MouseEventSource source, bool invertedScrolling)
{
//*********************************省略代码
e = new QWindowSystemInterfacePrivate::WheelEvent(window, timestamp, QHighDpi::fromNativeLocalPosition(local, window), QHighDpi::fromNativePixels(global, window), pixelDelta, angleDelta, angleDelta.y(), Qt::Vertical,
mods, phase, source, invertedScrolling);
return QWindowSystemInterfacePrivate::handleWindowSystemEvent(e);
//*********************************省略代码
}
↓
//将封装好的事件添加进windowSystemEventQueue
template<>
bool QWindowSystemInterfacePrivate::handleWindowSystemEvent<QWindowSystemInterface::AsynchronousDelivery>(WindowSystemEvent *ev)
{
windowSystemEventQueue.append(ev);
if (QAbstractEventDispatcher *dispatcher = QGuiApplicationPrivate::qt_qpa_core_dispatcher())
dispatcher->wakeUp();
return true;
}
参考事件封装调用栈:
QEventDispatcherWin32::processEvents qeventdispatcher_win.cpp
QWindowsGuiEventDispatcher::processEvents qwindowsguieventdispatcher.cpp
QEventLoop::processEvents qeventloop.cpp
QEventLoop::exec qeventloop.cpp
QCoreApplication::exec qcoreapplication.cpp
QGuiApplication::exec qguiapplication.cpp
QApplication::exec qapplication.cpp
事件的分发(事件的处理):
过程:
void QEventDispatcherWin32::wakeUp()
{
//*********************************省略代码
if (!PostMessage(d->internalHwnd, WM_QT_SENDPOSTEDEVENTS, 0, 0))
//*********************************省略代码
}
↓
LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp)
{
//*********************************省略代码
q->sendPostedEvents();
//*********************************省略代码
}
↓
//QWindowsGuiEventDispatcher子类重写了父类QEventDispatcherWin32的sendPostedEvents,如果直接从processEvents代码跳转会直接执行到QEventDispatcherWin32::sendPostedEvents,因此看不到GUI事件处理
void QWindowsGuiEventDispatcher::sendPostedEvents()
{
//*********************************省略代码
QWindowSystemInterface::sendWindowSystemEvents(m_flags);
}
↓
//通过QWindowSystemInterfacePrivate::getWindowSystemEvent()或QWindowSystemInterfacePrivate::getNonUserInputWindowSystemEvent()从windowSystemEventQueue获取添加进windowSystemEventQueue的事件
bool QWindowSystemInterface::sendWindowSystemEvents(QEventLoop::ProcessEventsFlags flags)
{
//*********************************省略代码
while (QWindowSystemInterfacePrivate::windowSystemEventsQueued()) {
QWindowSystemInterfacePrivate::WindowSystemEvent *event =
flags & QEventLoop::ExcludeUserInputEvents ?
QWindowSystemInterfacePrivate::getNonUserInputWindowSystemEvent() :
QWindowSystemInterfacePrivate::getWindowSystemEvent(); //从队列中获取出事件
//*********************************省略代码
if (QWindowSystemInterfacePrivate::eventHandler) {
if (QWindowSystemInterfacePrivate::eventHandler->sendEvent(event))
nevents++;
}
//*********************************省略代码
}
}
↓ 以Wheel类型为例
void QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePrivate::WindowSystemEvent *e)
{
Q_TRACE_SCOPE(QGuiApplicationPrivate_processWindowSystemEvent, e->type);
switch(e->type) {
//*********************************省略代码
case QWindowSystemInterfacePrivate::Wheel:
QGuiApplicationPrivate::processWheelEvent(static_cast<QWindowSystemInterfacePrivate::WheelEvent *>(e));
break;
//*********************************省略代码
}
}
↓
void QGuiApplicationPrivate::processWheelEvent(QWindowSystemInterfacePrivate::WheelEvent *e)
{
//*********************************省略代码
if (e->nullWindow()) {//如果事件中没有reciver
window = QGuiApplication::topLevelAt(globalPoint.toPoint());
if (window) {
QPointF delta = globalPoint - globalPoint.toPoint();
localPoint = window->mapFromGlobal(globalPoint.toPoint()) + delta;
}
}
//*********************************省略代码
#if QT_DEPRECATED_SINCE(5, 14)
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED //QWhellEvent封装
QWheelEvent ev(localPoint, globalPoint, e->pixelDelta, e->angleDelta, e->qt4Delta, e->qt4Orientation,
mouse_buttons, e->modifiers, e->phase, e->source, e->inverted);
QT_WARNING_POP
#else
QWheelEvent ev(localPoint, globalPoint, e->pixelDelta, e->angleDelta,
mouse_buttons, e->modifiers, e->phase, e->inverted, e->source);
#endif
ev.setTimestamp(e->timestamp);
QGuiApplication::sendSpontaneousEvent(window, &ev); //此处解释了reciver的来源和QWhellEvent封装
#else
Q_UNUSED(e);
#endif // QT_CONFIG(wheelevent)
}
↓
bool QCoreApplication::sendSpontaneousEvent(QObject *receiver, QEvent *event)
{
//*********************************省略代码
return notifyInternal2(receiver, event);
}
↓
bool QCoreApplication::notifyInternal2(QObject *receiver, QEvent *event)
{
//*********************************省略代码
return self->notify(receiver, event);
}
↓
bool QCoreApplication::notify(QObject *receiver, QEvent *event)
{
//*********************************省略代码
return doNotify(receiver, event);
}
↓
static bool doNotify(QObject *receiver, QEvent *event)
{
//*********************************省略代码
return receiver->isWidgetType() ? false : QCoreApplicationPrivate::notify_helper(receiver, event);
}
↓
bool QCoreApplicationPrivate::notify_helper(QObject *receiver, QEvent * event)
{
//*********************************省略代码
consumed = receiver->event(event);
return consumed;
}
参考调用栈:
MainWindow::mousePressEvent mainwindow.cpp
QWidget::event qwidget.cpp
QMainWindow::event qmainwindow.cpp
QApplicationPrivate::notify_helper qapplication.cpp
QApplication::notify qapplication.cpp
QCoreApplication::notifyInternal2 qcoreapplication.cpp
QCoreApplication::sendSpontaneousEvent qcoreapplication.cpp
QApplicationPrivate::sendMouseEvent qapplication.cpp
QWidgetWindow::handleMouseEvent qwidgetwindow.cpp
QWidgetWindow::event qwidgetwindow.cpp
QApplicationPrivate::notify_helper qapplication.cpp
QApplication::notify qapplication.cpp
QCoreApplication::notifyInternal2 qcoreapplication.cpp
QCoreApplication::sendSpontaneousEvent qcoreapplication.cpp
QGuiApplicationPrivate::processMouseEvent qguiapplication.cpp
QGuiApplicationPrivate::processWindowSystemEvent qguiapplication.cpp
QWindowSystemInterface::sendWindowSystemEvents qwindowsysteminterface.cpp
QWindowsGuiEventDispatcher::sendPostedEvents qwindowsguieventdispatcher.cpp
QEventDispatcherWin32::processEvents qeventdispatcher_win.cpp
QWindowsGuiEventDispatcher::processEvents qwindowsguieventdispatcher.cpp