深入了解事件处理系统对于每个学习Qt人来说非常重要,可以说,Qt是以事件驱动的UI工具集。 大家熟知Signals/Slots在多线程的实现也依赖于Qt的事件处理机制。
在Qt中,事件被封装成一个个对象,所有的事件均继承自抽象类QEvent
. 接下来依次谈谈Qt中有谁来产生、分发、接受和处理事件:
谁来产生事件:
最容易想到的是我们的输入设备,比如键盘、鼠标产生的
keyPressEvent
,keyReleaseEvent
,mousePressEvent
,mouseReleaseEvent
事件(他们被封装成QMouseEvent
和QKeyEvent
),这些事件来自于底层的操作系统,它们以异步的形式通知Qt事件处理系统,后文会仔细道来。当然Qt自己也会产生很多事件,比如QObject::startTimer()
会触发QTimerEvent
. 用户的程序可还以自己定制事件。
谁来接受和处理事件:
答案是QObject
。在Qt的内省机制剖析一文已经介绍QObject
类是整个Qt对象模型的心脏,事件处理机制是QObject
三大职责(内存管理、内省(intropection
)与事件处理制)之一。任何一个想要接受并处理事件的对象均须继承自QObject
,可以选择重载QObject::event()
函数或事件的处理权转给父类。
谁来负责分发事件:
对于non-GUI
的Qt程序,是由QCoreApplication
负责将QEvent
分发给QObject
的子类-Receiver
. 对于Qt GUI程序,由QApplication
来负责。
接下来,将通过对代码的解析来看看QT是利用事件循环从事件队列中获取用户输入事件,又是如何将事件转义成QEvents
,并分发给相应的QObjec
处理。
#include <QApplication>
#include "widget.h"
//Section 1
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Widget window; // Widget 继承自QWidget
window.show();
return app.exec(); // 进入Qpplication事件循环,见section 2
}
// Section 2:
int QApplication::exec()
{
//skip codes
//简单的交给QCoreApplication来处理事件循环=〉section 3
return QCoreApplication::exec();
}
// Section 3
int QCoreApplication::exec()
{
//得到当前Thread数据
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;
}
//检查event loop是否已经创建
if (!threadData->eventLoops.isEmpty()) {
qWarning("QCoreApplication::exec: The event loop is already running");
return -1;
}
...
QEventLoop eventLoop;
self->d_func()->in_exec = true;
self->d_func()->aboutToQuitEmitted = false;
//委任QEventLoop 处理事件队列循环 ==> Section 4
int returnCode = eventLoop.exec();
....
}
return returnCode;
}
// Section 4
int QEventLoop::exec(ProcessEventsFlags flags)
{
//这里的实现代码不少,最为重要的是以下几行
Q_D(QEventLoop); // 访问QEventloop私有类实例d
try {
//只要没有遇见exit,循环派发事件
while (!d->exit)
processEvents(flags | WaitForMoreEvents | EventLoopExec);
} catch (...) {}
}
// Section 5
bool QEventLoop::processEvents(ProcessEventsFlags flags)
{
Q_D(QEventLoop);
if (!d->threadData->eventDispatcher)
return false;
if (flags & DeferredDeletion)
QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
//将事件派发给与平台相关的QAbstractEventDispatcher子类 =>Section 6
return d->threadData->eventDispatcher->processEvents(flags);
}
// Section 6,QTDIR/src/corelib/kernel/qeventdispatcher_win.cpp
// 这段代码是完成与windows平台相关的windows c++。 以跨平台著称的Qt同时也提供了对Symiban,Unix等平台的消息派发支持
// 其事现分别封装在QEventDispatcherSymbian和QEventDispatcherUNIX
// QEventDispatcherWin32派生自QAbstractEventDispatcher.
bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
{
Q_D(QEventDispatcherWin32);
if (!d->internalHwnd)
createInternalHwnd();
d->interrupt = false;
emit awake();
bool canWait;
bool retVal = false;
bool seenWM_QT_SENDPOSTEDEVENTS = false;
bool needWM_QT_SENDPOSTEDEVENTS = false;
do {
DWORD waitRet = 0;
HANDLE pHandles[MAXIMUM_WAIT_OBJECTS - 1];
QVarLengthArray<MSG> processedTimers;
while (!d->interrupt) {
DWORD nCount = d->winEventNotifierList.count();
Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - 1);
MSG msg;
bool haveMessage;
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()) {
// 从处理socket队列中取出一条事件
haveMessage = true;
msg = d->queuedSocketEvents.takeFirst();
} else {
haveMessage = PeekMessage(&msg, 0, 0, 0, PM_REMOVE);
if (haveMessage && (flags & QEventLoop::ExcludeUserInputEvents)
&& ((msg.message >= WM_KEYFIRST
&& msg.message <= WM_KEYLAST)
|| (msg.message >= WM_MOUSEFIRST
&& msg.message <= WM_MOUSELAST)
|| msg.message == WM_MOUSEWHEEL
|| msg.message == WM_MOUSEHWHEEL
|| msg.message == WM_TOUCH
#ifndef QT_NO_GESTURES
|| msg.message == WM_GESTURE
|| msg.message == WM_GESTURENOTIFY
#endif
|| msg.message == WM_CLOSE)) {
// 用户输入事件入队列,待以后处理
haveMessage = false;
d->queuedUserInputEvents.append(msg);
}
if (haveMessage && (flags & QEventLoop::ExcludeSocketNotifiers)
&& (msg.message == WM_QT_SOCKETNOTIFIER && msg.hwnd == d->internalHwnd)) {
// socket 事件入队列,待以后处理
haveMessage = false;
d->queuedSocketEvents.append(msg);
}
}
....
if (!filterEvent(&msg)) {
TranslateMessage(&msg);
//将事件打包成message调用Windows API派发出去
//分发一个消息给窗口程序。消息被分发到回调函数,将消息传递给windows系统,windows处理完毕,会调用回调函数 => section 7
DispatchMessage(&msg);
}
}
}
} while (canWait);
...
return retVal;
}