赞
踩
Qt程序提供了事件循环机制,那么它是怎么工作的,工作原理又是什么呢?让我们通过qt源码揭开它神秘的面纱。
在开始之前,我们先思考下面几个问题:
1.事件循环是何时,以及如何启动的?
2.对于Windows,Qt是如何捕获事件(如:鼠标事件,键盘按键事件)?如何对捕获到的事件进行处理的?
3.如何将Windows消息转换为QEvent?
4.用户自定义的事件又是如何被处理的?
在Windows操作系统,所有的WIN32程序都建立在消息循环的基础之上,windows的消息机制可以分为以下几大步:
- 注册窗口过程处理函数
- 创建窗体
- show窗体
- 获取消息
- 翻译消息
- 分发消息至窗口处理函数
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
首先实例化QApplication对象,在构造QApplication时初始化threadData,如下所示:
父对象如果存在则使用父对象的threadData,表明和父对象使用同一个工作线程处理事件,反之通过QThreadData::current()拿到当前线程threadData。对于主事件循环来说,则通过QThreadData::current()拿到当前主线程,通过主线程处理事件。
class QThreadData { public: QThreadData(int initialRefCount = 1); ~QThreadData(); static Q_AUTOTEST_EXPORT QThreadData *current(bool createIfNecessary = true); /** * 省略 ....... */ public: int loopLevel; int scopeLevel; QStack<QEventLoop *> eventLoops; QPostEventList postEventList; // 待处理的事件列表 QAtomicPointer<QThread> thread; // 工作线程 QAtomicPointer<void> threadId; // 工作线程Id QAtomicPointer<QAbstractEventDispatcher> eventDispatcher; // 事件分发者,在不同平台下创建不同的事件分发者 QVector<void *> tls; FlaggedDebugSignatures flaggedSignatures; };
以windows为例,eventDispatcher的初始化:
事件分发者(eventDispatcher),初始化调用堆栈如上
Step 1
// src\corelib\kernel\qcoreapplication.cpp QCoreApplication::QCoreApplication(int &argc, char **argv #ifndef Q_QDOC , int _internal #endif ) #ifdef QT_NO_QOBJECT : d_ptr(new QCoreApplicationPrivate(argc, argv, _internal)) #else : QObject(*new QCoreApplicationPrivate(argc, argv, _internal)) #endif { d_func()->q_ptr = this; d_func()->init(); // 调用QCoreApplicationPrivate的init函数 #ifndef QT_NO_QOBJECT QCoreApplicationPrivate::eventDispatcher->startingUp(); #endif }
程序在启动时,通过main函数实例化QApplication对象。在QCoreApplication构造函数中调用QCoreApplicationPrivate的init函数初始参数
Step 2
// src\corelib\kernel\qcoreapplication.cpp void QCoreApplicationPrivate::init() { /** * 省略 ....... */ auto thisThreadData = threadData.loadRelaxed(); eventDispatcher = thisThreadData->eventDispatcher.loadRelaxed(); // otherwise we create one if (!eventDispatcher) createEventDispatcher(); // 调用createEventDispatcher创建事件分发者 Q_ASSERT(eventDispatcher); if (!eventDispatcher->parent()) { eventDispatcher->moveToThread(thisThreadData->thread.loadAcquire()); eventDispatcher->setParent(q); } thisThreadData->eventDispatcher = eventDispatcher; // 事件分发者赋值给threadData的eventDispatcher。当threadData.postEventList列表中有新加入的事件时,需要借助事件分发者向外投递一个消息,处理此事件 eventDispatcherReady(); /** * 省略 ....... */ }
在QCoreApplicationPrivate的init函数中,如果事件分发者没有创建则通过createEventDispatcher虚方发创建不同平台的事件分发者。
Step 3
// src\widgets\kernel\qapplication.cpp void QApplicationPrivate::createEventDispatcher() { QGuiApplicationPrivate::createEventDispatcher(); } // src\gui\kernel\qguiapplication.cpp void QGuiApplicationPrivate::createEventDispatcher() { Q_ASSERT(!eventDispatcher); if (platform_integration == nullptr) createPlatformIntegration(); // 创建平台 // The platform integration should not mess with the event dispatcher Q_ASSERT(!eventDispatcher); eventDispatcher = platform_integration->createEventDispatcher(); // 通过对应平台的事件分发者 }
上段代码需要注意的是createEventDispatcher是一个虚方法,并且在每个类中都有实现。在此处则调用是QApplicationPrivate的该方法。在此处创建QWindowsIntegration以及QWindowsGuiEventDispatcher
Step 4
// src\gui\kernel\qguiapplication.cpp void QGuiApplicationPrivate::createPlatformIntegration() { QHighDpiScaling::initHighDpiScaling(); // Load the platform integration QString platformPluginPath = QString::fromLocal8Bit(qgetenv("QT_QPA_PLATFORM_PLUGIN_PATH")); /** * 代码省略 ....... * 这部分代码通过从环境变量或者从命令行参数中解析平台名称,插件路径,插件名称 */ init_platform(QLatin1String(platformName), platformPluginPath, platformThemeName, argc, argv); // 创建平台插件 if (!icon.isEmpty()) forcedWindowIcon = QDir::isAbsolutePath(icon) ? QIcon(icon) : QIcon::fromTheme(icon); } static void init_platform(const QString &pluginNamesWithArguments, const QString &platformPluginPath, const QString &platformThemeName, int &argc, char **argv) { QStringList plugins = pluginNamesWithArguments.split(QLatin1Char(';')); QStringList platformArguments; QStringList availablePlugins = QPlatformIntegrationFactory::keys(platformPluginPath); for (const auto &pluginArgument : plugins) { // Split into platform name and arguments QStringList arguments = pluginArgument.split(QLatin1Char(':')); const QString name = arguments.takeFirst().toLower(); QString argumentsKey = name; argumentsKey[0] = argumentsKey.at(0).toUpper(); arguments.append(QLibraryInfo::platformPluginArguments(argumentsKey)); // Create the platform integration. QGuiApplicationPrivate::platform_integration = QPlatformIntegrationFactory::create(name, arguments, argc, argv, platformPluginPath); // 通过插件工具,创建平台插件 if (Q_UNLIKELY(!QGuiApplicationPrivate::platform_integration)) { if (availablePlugins.contains(name)) { qCInfo(lcQpaPluginLoading).nospace().noquote() << "Could not load the Qt platform plugin \"" << name << "\" in \"" << QDir::toNativeSeparators(platformPluginPath) << "\" even though it was found."; } else { qCWarning(lcQpaPluginLoading).nospace().noquote() << "Could not find the Qt platform plugin \"" << name << "\" in \"" << QDir::toNativeSeparators(platformPluginPath) << "\""; } } else { QGuiApplicationPrivate::platform_name = new QString(name); platformArguments = arguments; break; } } /** * 代码省略 ....... */ }
从环境变量或者从命令行参数中解析平台名称,插件路径,插件名称。将插件路径下的所有插件放入到列表中,遍历插件列表加载对应平台的插件。如在windows上则加载的是qwindows.dll
插件位于安装目录下如:C:\Qt\5.15.2\msvc2019_64\plugins\platforms
windows下qt程序包插件位于执行目录下的platforms目录下:
在linux平台下,通过QT_QPA_PLATFORM_PLUGIN_PATH环境变量加载平台插件(因为我是在交叉编译环境上运行,所以是qeglfs):
Step 5
// src\gui\kernel\qplatformintegrationfactory.cpp QPlatformIntegration *QPlatformIntegrationFactory::create(const QString &platform, const QStringList ¶mList, int &argc, char **argv, const QString &platformPluginPath) { #if QT_CONFIG(library) // Try loading the plugin from platformPluginPath first: if (!platformPluginPath.isEmpty()) { QCoreApplication::addLibraryPath(platformPluginPath); if (QPlatformIntegration *ret = qLoadPlugin<QPlatformIntegration, QPlatformIntegrationPlugin>(directLoader(), platform, paramList, argc, argv)) return ret; } #else Q_UNUSED(platformPluginPath); #endif return qLoadPlugin<QPlatformIntegration, QPlatformIntegrationPlugin>(loader(), platform, paramList, argc, argv); } // src\corelib\plugin\qfactoryloader_p.h template <class PluginInterface, class FactoryInterface, typename ...Args> PluginInterface *qLoadPlugin(const QFactoryLoader *loader, const QString &key, Args &&...args) { const int index = loader->indexOf(key); if (index != -1) { QObject *factoryObject = loader->instance(index); // 实例化平台的插件对象。此处在windows平台下,实际上是实例化QWindowsIntegrationPlugin对象 if (FactoryInterface *factory = qobject_cast<FactoryInterface *>(factoryObject)) if (PluginInterface *result = factory->create(key, std::forward<Args>(args)...)) return result; } return nullptr; } // src\plugins\platforms\windows\main.cpp class QWindowsIntegrationPlugin : public QPlatformIntegrationPlugin { Q_OBJECT Q_PLUGIN_METADATA(IID QPlatformIntegrationFactoryInterface_iid FILE "windows.json") // 很关键,提供插件的导出符号。 public: QPlatformIntegration *create(const QString&, const QStringList&, int &, char **); }; QPlatformIntegration *QWindowsIntegrationPlugin::create(const QString& system, const QStringList& paramList, int &, char **) { if (system.compare(system, QLatin1String("windows"), Qt::CaseInsensitive) == 0) return new QWindowsGdiIntegration(paramList); // 创建QWindowsGdiIntegration return nullptr; }
通过qt的插件机制,加载插件。通过插件提供的qt_plugin_instance接口实例化QWindowsIntegrationPlugin,然后创建QWindowsGdiIntegration
通过depends分析qwindows.dll插件,向外提供两个接口qt_plugin_instance和qt_plugin_query_metadata
Step 6
// rc\plugins\platforms\windows\qwindowsintegration.cpp
QAbstractEventDispatcher * QWindowsIntegration::createEventDispatcher() const
{
return new QWindowsGuiEventDispatcher;
}
QWindowsGdiIntegration继承QWindowsIntegration,因此调用createEventDispatcher创建事件分发者,创建的是QWindowsGuiEventDispatcher
Step 1
// src\corelib\kernel\qcoreapplication.cpp int QCoreApplication::exec() { if (!QCoreApplicationPrivate::checkInstance("exec")) return -1; QThreadData *threadData = self->d_func()->threadData; if (threadData != QThreadData::current()) { // 如果不是主线程,则退出 qWarning("%s::exec: Must be called from the main thread", self->metaObject()->className()); return -1; } if (!threadData->eventLoops.isEmpty()) { qWarning("QCoreApplication::exec: The event loop is already running"); return -1; } threadData->quitNow = false; QEventLoop eventLoop; self->d_func()->in_exec = true; self->d_func()->aboutToQuitEmitted = false; int returnCode = eventLoop.exec(); // 进入事件循环 threadData->quitNow = false; if (self) self->d_func()->execCleanup(); return returnCode; }
在main函数中调用QApplication的exec函数,最终调用的是QCoreApplication的exec函数,在QCoreApplication的exec函数中,实例化QEventLoop对象,通过QEventLoop的exec进入到事件循环
Step 2
// src\corelib\kernel\qeventloop.cpp 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); // 调用事件分发者的processEvents,(本例事件分发者为QEventDispatcherWin32) }
d->exit.loadAcquire()为事件循环的状态,在不退出事件循环时,此变量一直为false。通过while循环调用事件分发者的processEvents去处理事件
Step 3
// src\corelib\kernel\qeventdispatcher_win.cpp bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags) { Q_D(QEventDispatcherWin32); if (!d->internalHwnd) { createInternalHwnd(); // d->internalHwnd作为接收windows消息的窗体,如果窗体不存在则新创建一个 wakeUp(); // trigger a call to sendPostedEvents() } /** * 省略 ....... */ } void QEventDispatcherWin32::createInternalHwnd() { Q_D(QEventDispatcherWin32); if (d->internalHwnd) return; d->internalHwnd = qt_create_internal_window(this); // 创建窗体并赋值给d->internalHwnd句柄 // setup GetMessage hook needed to drive our posted events d->getMessageHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC) qt_GetMessageHook, NULL, GetCurrentThreadId()); if (Q_UNLIKELY(!d->getMessageHook)) { int errorCode = GetLastError(); qFatal("Qt: INTERNAL ERROR: failed to install GetMessage hook: %d, %ls", errorCode, qUtf16Printable(qt_error_string(errorCode))); } // start all normal timers for (int i = 0; i < d->timerVec.count(); ++i) d->registerTimer(d->timerVec.at(i)); }
事件分发者第一次调用processEvents处理事件时,d->internalHwnd窗口句柄为空。接下来准备调用操作系统API创建窗口,注册窗口过程处理函数。
Step 4
// step 5 调用qWindowsMessageWindowClassContext注册消息过程处理函数,并且通过WIN API CreateWindow创建窗体 // src\corelib\kernel\qeventdispatcher_win.cpp static HWND qt_create_internal_window(const QEventDispatcherWin32 *eventDispatcher) { QWindowsMessageWindowClassContext *ctx = qWindowsMessageWindowClassContext(); // 注册消息过程处理函数 if (!ctx->atom) return 0; // 创建窗体 HWND wnd = CreateWindow(ctx->className, // classname ctx->className, // window name 0, // style 0, 0, 0, 0, // geometry HWND_MESSAGE, // parent 0, // menu handle GetModuleHandle(0), // application 0); // windows creation data. if (!wnd) { qErrnoWarning("CreateWindow() for QEventDispatcherWin32 internal window failed"); return 0; } #ifdef GWLP_USERDATA SetWindowLongPtr(wnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(eventDispatcher)); #else SetWindowLong(wnd, GWL_USERDATA, reinterpret_cast<LONG>(eventDispatcher)); #endif return wnd; }
在qWindowsMessageWindowClassContext中注册窗口过程处理函数,调用windows API CreateWindow创建窗口
Step 5
// src\corelib\kernel\qeventdispatcher_win.cpp Q_GLOBAL_STATIC(QWindowsMessageWindowClassContext, qWindowsMessageWindowClassContext) // Q_GLOBAL_STATIC宏通过QGlobalStatic将QWindowsMessageWindowClassContext包装,并创建一个名为qWindowsMessageWindowClassContext的QGlobalStatic对象 #define Q_GLOBAL_STATIC(TYPE, NAME) \ Q_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ()) // 通过宏Q_GLOBAL_STATIC_WITH_ARGS创建一个名为NAME全局变量 #define Q_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ARGS) \ namespace { namespace Q_QGS_ ## NAME { \ typedef TYPE Type; \ QBasicAtomicInt guard = Q_BASIC_ATOMIC_INITIALIZER(QtGlobalStatic::Uninitialized); \ Q_GLOBAL_STATIC_INTERNAL(ARGS) \ } } \ static QGlobalStatic<TYPE, \ Q_QGS_ ## NAME::innerFunction, \ Q_QGS_ ## NAME::guard> NAME; // 通过Q_GLOBAL_STATIC_INTERNAL定义一个返回值为Type* 名为innerFunction的函数,该函数通过guard判断静态变量d(使用静态变量的好处是延长变量生命周期;在下次进入该函数时,变量仍为上次的值)是否被初始化过,如果没有被初始化则创建一个新的,反之直接return变量d #define Q_GLOBAL_STATIC_INTERNAL(ARGS) \ Q_DECL_HIDDEN inline Type *innerFunction() \ { \ static Type *d; \ static QBasicMutex mutex; \ int x = guard.loadAcquire(); \ if (Q_UNLIKELY(x >= QtGlobalStatic::Uninitialized)) { \ const std::lock_guard<QBasicMutex> locker(mutex); \ if (guard.loadRelaxed() == QtGlobalStatic::Uninitialized) { \ d = new Type ARGS; \ static struct Cleanup { \ ~Cleanup() { \ delete d; \ guard.storeRelaxed(QtGlobalStatic::Destroyed); \ } \ } cleanup; \ guard.storeRelease(QtGlobalStatic::Initialized); \ } \ } \ return d; \ } // 声明类型为T,通过innerFunction函数指针创建T类型静态变量,并通过guard来判断静态变量的状态 template <typename T, T *(&innerFunction)(), QBasicAtomicInt &guard> struct QGlobalStatic { typedef T Type; bool isDestroyed() const { return guard.loadRelaxed() <= QtGlobalStatic::Destroyed; } bool exists() const { return guard.loadRelaxed() == QtGlobalStatic::Initialized; } operator Type *() { if (isDestroyed()) return nullptr; return innerFunction(); } Type *operator()() { if (isDestroyed()) return nullptr; return innerFunction(); }// 重载(),并通过innerFunction函数指针获取静态变量 Type *operator->() { Q_ASSERT_X(!isDestroyed(), "Q_GLOBAL_STATIC", "The global static was used after being destroyed"); return innerFunction(); } Type &operator*() { Q_ASSERT_X(!isDestroyed(), "Q_GLOBAL_STATIC", "The global static was used after being destroyed"); return *innerFunction(); } }; /********************************************************************************************************/ // 综上step 4的QWindowsMessageWindowClassContext *ctx = qWindowsMessageWindowClassContext(); 则为:创建类型为QWindowsMessageWindowClassContext的静态变量,并通过qWindowsMessageWindowClassContext()返回其指针赋值给ctx变量 // src\corelib\kernel\qeventdispatcher_win.cpp QWindowsMessageWindowClassContext::QWindowsMessageWindowClassContext() : atom(0), className(0) { /** * 省略 ....... */ WNDCLASS wc; wc.style = 0; wc.lpfnWndProc = qt_internal_proc; // 消息处理回调函数 wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = GetModuleHandle(0); wc.hIcon = 0; wc.hCursor = 0; wc.hbrBackground = 0; wc.lpszMenuName = NULL; wc.lpszClassName = className; atom = RegisterClass(&wc); // 注册消息过程处理函数 if (!atom) { qErrnoWarning("%ls RegisterClass() failed", qUtf16Printable(qClassName)); delete [] className; className = 0; } }
通过实例化QWindowsMessageWindowClassContext注册消息过程处理函数
Step 6
// src\corelib\kernel\qeventdispatcher_win.cpp LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp) { if (message == WM_NCCREATE) return true; MSG msg; msg.hwnd = hwnd; msg.message = message; msg.wParam = wp; msg.lParam = lp; QAbstractEventDispatcher* dispatcher = QAbstractEventDispatcher::instance(); #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) qintptr result; #else long result; #endif if (!dispatcher) { if (message == WM_TIMER) KillTimer(hwnd, wp); return 0; } if (dispatcher->filterNativeEvent(QByteArrayLiteral("windows_dispatcher_MSG"), &msg, &result)) // 系统原生消息的过滤器 return result; #ifdef GWLP_USERDATA auto q = reinterpret_cast<QEventDispatcherWin32 *>(GetWindowLongPtr(hwnd, GWLP_USERDATA)); #else auto q = reinterpret_cast<QEventDispatcherWin32 *>(GetWindowLong(hwnd, GWL_USERDATA)); #endif QEventDispatcherWin32Private *d = 0; if (q != 0) d = q->d_func(); switch (message) { case WM_QT_SOCKETNOTIFIER: /** * 省略 ....... */ case WM_QT_ACTIVATENOTIFIERS: { /** * 省略 ....... */ } case WM_TIMER: /** * 省略 ....... */ case WM_QT_SENDPOSTEDEVENTS: // 通过调用PostEvent投递的事件会被直接处理 Q_ASSERT(d != 0); // We send posted events manually, if the window procedure was invoked // by the foreign event loop (e.g. from the native modal dialog). // Skip sending, if the message queue is not empty. // sendPostedEventsTimer will deliver posted events later. static const UINT mask = inputQueueMask(); if (HIWORD(GetQueueStatus(mask)) == 0) q->sendPostedEvents(); return 0; } // switch (message) return DefWindowProc(hwnd, message, wp, lp); }
在窗口过程处理函数中,先通过filterNativeEvent对原生消息处理,然后再对不同的消息进行处理。
Step 7
// step 9通过PeekMessage获取系统消息,通过TranslateMessage、DispatchMessage分发消息 // src\corelib\kernel\qeventdispatcher_win.cpp bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags) { Q_D(QEventDispatcherWin32); if (!d->internalHwnd) { createInternalHwnd(); wakeUp(); // trigger a call to sendPostedEvents() } d->interrupt.storeRelaxed(false); emit awake(); // To prevent livelocks, send posted events once per iteration. // QCoreApplication::sendPostedEvents() takes care about recursions. sendPostedEvents(); // 处理postEvent队列的事件和用户输入事件 auto threadData = d->threadData.loadRelaxed(); bool canWait; bool retVal = false; do { DWORD waitRet = 0; DWORD nCount = 0; HANDLE *pHandles = nullptr; if (d->winEventNotifierActivatedEvent) { nCount = 1; pHandles = &d->winEventNotifierActivatedEvent; } QVarLengthArray<MSG> processedTimers; while (!d->interrupt.loadRelaxed()) { MSG msg; bool haveMessage; // 队列中有消息则取一个MSG if (!(flags & QEventLoop::ExcludeUserInputEvents) && !d->queuedUserInputEvents.isEmpty()) { // process queued user input events haveMessage = true; msg = d->queuedUserInputEvents.takeFirst(); } else if(!(flags & QEventLoop::ExcludeSocketNotifiers) && !d->queuedSocketEvents.isEmpty()) { // process queued socket events haveMessage = true; msg = d->queuedSocketEvents.takeFirst(); } else { // 队列中没有消息,读取消息,并将消息放入队列 haveMessage = PeekMessage(&msg, 0, 0, 0, PM_REMOVE); if (haveMessage) { if (flags.testFlag(QEventLoop::ExcludeUserInputEvents) && isUserInputMessage(msg.message)) { // queue user input events for later processing d->queuedUserInputEvents.append(msg); continue; } if ((flags & QEventLoop::ExcludeSocketNotifiers) && (msg.message == WM_QT_SOCKETNOTIFIER && msg.hwnd == d->internalHwnd)) { // queue socket events for later processing d->queuedSocketEvents.append(msg); continue; } } } if (!haveMessage) { // no message - check for signalled objects waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, 0, QS_ALLINPUT, MWMO_ALERTABLE);// 设置超时时间等待消息 if ((haveMessage = (waitRet == WAIT_OBJECT_0 + nCount))) { // a new message has arrived, process it continue; } } if (haveMessage) { // 如果消息是内部窗口句柄并且消息号是WM_QT_SENDPOSTEDEVENTS(wakeUp()通过调用PostMessage投递给d->internalHwnd窗口WM_QT_SENDPOSTEDEVENTS消息) // 存在一个疑问:如果一直在往系统中投递WM_QT_SENDPOSTEDEVENTS消息,那么这个循环就永远跳不出去,会不会存在投递的事件一直没有被处理??? if (d->internalHwnd == msg.hwnd && msg.message == WM_QT_SENDPOSTEDEVENTS) { // Set result to 'true' because the message was sent by wakeUp(). retVal = true; continue; } if (msg.message == WM_TIMER) { // Skip timer event intended for use inside foreign loop. if (d->internalHwnd == msg.hwnd && msg.wParam == d->sendPostedEventsTimerId) continue; // avoid live-lock by keeping track of the timers we've already sent bool found = false; for (int i = 0; !found && i < processedTimers.count(); ++i) { const MSG processed = processedTimers.constData()[i]; found = (processed.wParam == msg.wParam && processed.hwnd == msg.hwnd && processed.lParam == msg.lParam); } if (found) continue; processedTimers.append(msg); } else if (msg.message == WM_QUIT) { if (QCoreApplication::instance()) QCoreApplication::instance()->quit(); return false; } if (!filterNativeEvent(QByteArrayLiteral("windows_generic_MSG"), &msg, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); // 分发消息至窗口过程处理函数 } } else if (waitRet - WAIT_OBJECT_0 < nCount) { activateEventNotifiers(); } else { // nothing todo so break break; } retVal = true; } // still nothing - wait for message or signalled objects canWait = (!retVal && !d->interrupt.loadRelaxed() && flags.testFlag(QEventLoop::WaitForMoreEvents) && threadData->canWaitLocked()); if (canWait) { emit aboutToBlock(); waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE); emit awake(); if (waitRet - WAIT_OBJECT_0 < nCount) { activateEventNotifiers(); retVal = true; } } } while (canWait); return retVal; }
至此对于一个Qt GUI程序的事件循环已经启动完毕
用户通过sendEvent投递一个自定义事件,sendEvent的工作原理又是怎样的
Step 1
// src\corelib\kernel\qcoreapplication.cpp bool QCoreApplication::sendEvent(QObject *receiver, QEvent *event) { Q_TRACE(QCoreApplication_sendEvent, receiver, event, event->type()); if (event) event->spont = false; return notifyInternal2(receiver, event); } bool QCoreApplication::notifyInternal2(QObject *receiver, QEvent *event) { bool selfRequired = QCoreApplicationPrivate::threadRequiresCoreApplication(); if (!self && selfRequired) return false; // Make it possible for Qt Script to hook into events even // though QApplication is subclassed... bool result = false; void *cbdata[] = { receiver, event, &result }; if (QInternal::activateCallbacks(QInternal::EventNotifyCallback, cbdata)) { return result; } // Qt enforces the rule that events can only be sent to objects in // the current thread, so receiver->d_func()->threadData is // equivalent to QThreadData::current(), just without the function // call overhead. QObjectPrivate *d = receiver->d_func(); QThreadData *threadData = d->threadData; QScopedScopeLevelCounter scopeLevelCounter(threadData); if (!selfRequired) return doNotify(receiver, event); return self->notify(receiver, event); }
通过QApplication::sendEvent投递一个事件,实际上调用QCoreApplication::sendEvent,QApplication继承QCoreApplication。在notifyInternal2中self->notify(receiver, event),实际上调用的是QApplication的notify,notify为虚函数在QApplication中重写
Step 2
// src\widgets\kernel\qapplication.cpp bool QApplication::notify(QObject *receiver, QEvent *e) { /** * 代码省略 ....... * 如果事件类型是qt定义的事件类型,则按照对应的事件处理逻辑去处理。如果是自定义的事件类型,则调用notify_helper去处理 */ res = d->notify_helper(receiver, e); return res; } bool QApplicationPrivate::notify_helper(QObject *receiver, QEvent * e) { /** * 代码省略 ....... */ // send to all receiver event filters if (sendThroughObjectEventFilters(receiver, e)) { // 先给事件过滤器处理事件 filtered = true; return filtered; } // deliver the event consumed = receiver->event(e); // 交给event去处理 QCoreApplicationPrivate::setEventSpontaneous(e, false); return consumed; }
在QApplication::notify中如果事件类型是qt定义的事件类型,则按照对应的事件处理逻辑去处理。如果是自定义的事件类型,则调用notify_helper去处理。在notify_helper中处理事件是,先通过事件处理器处理,最后交给目标对象的event处理。
在此可以看见事件的处理顺序为:QApplication::notify > eventFilter > event
Step 1
// src\corelib\kernel\qcoreapplication.cpp void QCoreApplication::postEvent(QObject *receiver, QEvent *event, int priority) { /** * 代码省略 ....... */ // delete the event on exceptions to protect against memory leaks till the event is // properly owned in the postEventList QScopedPointer<QEvent> eventDeleter(event); // 在栈上创建一个智能指针,eventDeleter销毁时自动释放event对象,防止内存泄露 Q_TRACE(QCoreApplication_postEvent_event_posted, receiver, event, event->type()); data->postEventList.addEvent(QPostEvent(receiver, event, priority)); // 将投递的事件添加postEventList列表中 eventDeleter.take(); event->posted = true; ++receiver->d_func()->postedEvents; data->canWait = false; locker.unlock(); QAbstractEventDispatcher* dispatcher = data->eventDispatcher.loadAcquire(); if (dispatcher) dispatcher->wakeUp(); // 通知postEventList队列里面添加了一个新的事件 } // src\corelib\kernel\qeventdispatcher_win.cpp void QEventDispatcherWin32::wakeUp() { Q_D(QEventDispatcherWin32); if (d->internalHwnd && d->wakeUps.testAndSetAcquire(0, 1)) { // post a WM_QT_SENDPOSTEDEVENTS to this thread if there isn't one already pending if (!PostMessage(d->internalHwnd, WM_QT_SENDPOSTEDEVENTS, 0, 0)) // 调用系统接口,投递一个WM_QT_SENDPOSTEDEVENTS消息 qErrnoWarning("QEventDispatcherWin32::wakeUp: Failed to post a message"); } }
调用postEvent将投递的事件放入到postEventList队列中,并通过PostMessage向系统投递一个WM_QT_SENDPOSTEDEVENTS消息,用于通知有新的事件添加到队列中。事件循环会从系统获取到一个WM_QT_SENDPOSTEDEVENTS消息,然后去处理postEventList队列的消息。
Step 2
// src\platformsupport\eventdispatchers\qwindowsguieventdispatcher.cpp void QWindowsGuiEventDispatcher::sendPostedEvents() { QEventDispatcherWin32::sendPostedEvents(); QWindowSystemInterface::sendWindowSystemEvents(m_flags); } // src\corelib\kernel\qeventdispatcher_win.cpp void QEventDispatcherWin32::sendPostedEvents() { Q_D(QEventDispatcherWin32); if (d->sendPostedEventsTimerId != 0) KillTimer(d->internalHwnd, d->sendPostedEventsTimerId); d->sendPostedEventsTimerId = 0; // Allow posting WM_QT_SENDPOSTEDEVENTS message. d->wakeUps.storeRelaxed(0); QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData.loadRelaxed()); } // void QCoreApplicationPrivate::sendPostedEvents(QObject *receiver, int event_type, QThreadData *data) { /** * 代码省略 ....... */ while (i < data->postEventList.size()) { // 遍历postEventList // avoid live-lock if (i >= data->postEventList.insertionOffset) break; const QPostEvent &pe = data->postEventList.at(i); // 从postEventList队列中取一个事件 ++i; if (!pe.event) continue; if ((receiver && receiver != pe.receiver) || (event_type && event_type != pe.event->type())) { data->canWait = false; continue; } if (pe.event->type() == QEvent::DeferredDelete) { // DeferredDelete events are sent either // 1) when the event loop that posted the event has returned; or // 2) if explicitly requested (with QEvent::DeferredDelete) for // events posted by the current event loop; or // 3) if the event was posted before the outermost event loop. int eventLevel = static_cast<QDeferredDeleteEvent *>(pe.event)->loopLevel(); int loopLevel = data->loopLevel + data->scopeLevel; const bool allowDeferredDelete = (eventLevel > loopLevel || (!eventLevel && loopLevel > 0) || (event_type == QEvent::DeferredDelete && eventLevel == loopLevel)); if (!allowDeferredDelete) { // cannot send deferred delete if (!event_type && !receiver) { // we must copy it first; we want to re-post the event // with the event pointer intact, but we can't delay // nulling the event ptr until after re-posting, as // addEvent may invalidate pe. QPostEvent pe_copy = pe; // null out the event so if sendPostedEvents recurses, it // will ignore this one, as it's been re-posted. const_cast<QPostEvent &>(pe).event = nullptr; // re-post the copied event so it isn't lost data->postEventList.addEvent(pe_copy); } continue; } } // first, we diddle the event so that we can deliver // it, and that no one will try to touch it later. pe.event->posted = false; QEvent *e = pe.event; QObject * r = pe.receiver; --r->d_func()->postedEvents; Q_ASSERT(r->d_func()->postedEvents >= 0); // next, update the data structure so that we're ready // for the next event. const_cast<QPostEvent &>(pe).event = nullptr; locker.unlock(); const auto relocker = qScopeGuard([&locker] { locker.lock(); }); QScopedPointer<QEvent> event_deleter(e); // will delete the event (with the mutex unlocked) // after all that work, it's time to deliver the event. QCoreApplication::sendEvent(r, e); // 最后按照sendEvent的方式将事件处理掉 // careful when adding anything below this point - the // sendEvent() call might invalidate any invariants this // function depends on. } }
事件循环调用sendPostedEvents从而遍历整个队列的事件,将所有的事件按照sendEvent的方式处理掉。
1.sendEvent为同步接口,从源码上看,sendEvent整个过程都为函数调用
2.postEvent为异步接口,因为postEevent先将事件添加到postevent队列中,然后再调用操作系统的异步接口PostMessage向系统投递一个消息,最后在消息循环中拿到此消息,才会去从postEevent队列中取出一个事件进行处理
至此,针对本文开头所提出的四个问题:
1.第一个问题,在事件循环初始化,启动事件循环(其实只完成了一半,因为处理用户输入事件的消息还未讲)已给出答案
2.第二个问题,至此未给出答案,在接下来的文章中会给出答案,敬请期待
3.第三个问题,至此未给出答案,在接下来的文章中会给出答案,敬请期待
4.第四个问题,sendEvent和postEvent已给出答案
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。