QCoreApplication 是为了编写 console 下 Qt 程序设计的类,提供 event loop,QApplication 继承了它,提供 GUI 下的的 event loop。我们先看看 QCoreApplication,这个类应该是一个 singleton,在其构造函数中调用的 init() 里面有如下代码
Q_ASSERT_X(!self, "QCoreApplication", "there should be only one application object"); QCoreApplication::self = this;注意与此对应,event dispatcher 对于一个线程也应该是唯一的,
// use the event dispatcher created by the app programmer (if any) if (!QCoreApplicationPrivate::eventDispatcher) QCoreApplicationPrivate::eventDispatcher = d->threadData->eventDispatcher; // otherwise we create one if (!QCoreApplicationPrivate::eventDispatcher) d->createEventDispatcher(); Q_ASSERT(QCoreApplicationPrivate::eventDispatcher != 0); if (!QCoreApplicationPrivate::eventDispatcher->parent()) QCoreApplicationPrivate::eventDispatcher->moveToThread(d->threadData->thread); d->threadData->eventDispatcher = QCoreApplicationPrivate::eventDispatcher;类似的,使用 QApplication 时,我们建立了 event dispatcher,创建了 widget 并 QWidget::show() 了,当我们调用 QApplication::exec() 时,进入消息循环,可是消息如何发给我们的 widget 的呢?我们知道前面的 main.cpp 里面甚至没有 QApplication 和 QWidget 的任何关联。 事实是这样的,我们建立一个 QCoreApplication(包括一个 QApplication)对象时,同时建立的 QEventDispatcher 为本 thread 做好了接受 Events 的准备,QWidget 负责创建一个窗体,创建这个部分需要调用底层的 xlib,这时 QEventDispatcher 在该线程某个 xlib 创建的部分收到 X11 的事件后就会获得 event,QEventDispatcherX11::processEvents() 将某一部分 event (用户输入事件)放到一个队列中,还有一部份交给 QApplication::x11PrcessEvent(),该函数负责找到对应的 QWidget 并按照事件类型通过 QWidget::x11Event() 发送过去,注意该函数默认返回 false,
bool QWidget::x11Event(XEvent *)
{
return false;
}这是为了让程序员需要的时候自己处理 X11 的事件,因此可以继承 QWidget 覆盖这个函数。我们看看进入消息队列的那部分,switch (event.type) {
case ButtonPress:
case ButtonRelease:
case MotionNotify:
case XKeyPress:
case XKeyRelease:
case EnterNotify:
case LeaveNotify:
d->queuedUserInputEvents.append(event);
continue;
// ...可见我们需要看看 QEventDispatcherX11Private 维护的 queuedUserInputEvents 队列是如何被处理的,class QEventDispatcherX11Private : public QEventDispatcherUNIXPrivate
{
Q_DECLARE_PUBLIC(QEventDispatcherX11)
public:
inline QEventDispatcherX11Private()
: xfd(-1)
{ }
int xfd;
QList<XEvent> queuedUserInputEvents;
};这是一个链表,事实上 QApplication 调用 processEvents() 时,即调用了 QEventDispatcher::processEvents()。这只是处理消息的函数,我们仍然不清楚该函数如何被调用的,在 QCoreApplication::exec() 里,另外一个东西就是 QEventLoop,在 QEventLoop::exec() 里面我们找到了答案,// remove posted quit events when entering a new event loop if (qApp->thread() == thread()) QCoreApplication::removePostedEvents(qApp, QEvent::Quit); while (!d->exit) processEvents(flags | WaitForMoreEvents | EventLoopExec);这就是核心的部分。
上面的过程比较混乱,这里重新总结下,建立的 QCoreApplication/QApplication 建立了一个 QEventDispatcher,然后建立了用户界面后就可以响应用户输入了,调用了 QApplication::exec() 后,这部分 X event 通过 Xlib 发送过来,由 QEventLoop 处理消息,调用的是 QEventDispatcher::processEvents() 维护消息队列,该函数会通过 QApplication::x11ProcessEvent() 将消息发送给对应的 QWidget,也可以通过设置用户自己处理 XEvent,这会发送给那个 QWidget 对应的 x11Event() 函数处理。
这也就是为什么写多线程程序时,如果希望在新的线程使用消息机制,也需要在 QThread::run() 里面调用 QThread::exec() 维护消息队列。