Qt事件循环详解
事件循环简介
UI程序之所叫UI程序,是因为需要与用户有交互,用户交互一般是通过鼠标键盘等的输入设备,那UI程序就需要有能随时响应用户交互的能力。
一个C++程序的main函数大概是下面这样:
int main()
{
...
return 0;
}
我们如何使程序能随时可以响应用户交互呢?一个比较简单的设计就是通过while循环:
int main()
{
while (1) {
获取用户输入;
处理用户输入;
}
return 0;
}
有时一个事件的处理可能稍微会多花一点时间,如果是上面这样,在处理一个事件时就不能响应其他事件了,所以我们需要一个队列,系统可以将新事件放到队列里:
int main()
{
while(1) {
event = getEventFromQueue()
dealEvent(event);
}
}
上面就是一个简单的事件循环。
QT事件循环过程
QT框架的事件循环主体框架与上面类似,不过要复杂的多。
下面来简单看下事件循环过程,这里主要以追踪方法调用的来了解事件循环过程:
启动事件循环
QT程序中main函数中的app.exec()
实际就是启动事件循环:
int QCoreApplication::exec()
{
...
QEventLoop eventLoop;
eventLoop.exec();
...
}
可以看到实际是创建了QEventLoop事件循环对象来开启事件循环。
有时我们阻塞调用某个方法,但又不想干扰事件循环时,可以创建QEventLoop来处理事件,这个我们后面再说。
QEventLoop事件循环
这里就可以看到一个while循环了,如果循环退出标志位为true,循环将一直进行下去。
int QEventLoop::exec(ProcessEventsFlags flags)
{
...
while (!d->exit.loadAcquire())
processEvents(flags | WaitForMoreEvents | EventLoopExec);
...
}
bool QEventLoop::processEvents(ProcessEventsFlags flags)
{
Q_D(QEventLoop);
if (!d->threadData->hasEventDispatcher())
return false;
return d->threadData->eventDispatcher.loadRelaxed()->processEvents(flags);
}
QEventLoop::processEvents
实际调用的是本线程的事件分发对象来处理事件。
不同系统的事件处理方式不一样,所以会有多个从事件分发基类QAbstractEventDispatcher派生的子类,包括QEventDispatcherUNIX
、QEventDispatcherWin32
等,这里我们以linux系统下的QEventDispatcherUNIX
为例来分析。
QEventDispatcherUNIX事件处理
顾名思义,QEventDispatcherUNIX
就是UNIX平台下的事件分发器,我们来看代码:
bool QEventDispatcherUNIX::processEvents(QEventLoop::ProcessEventsFlags flags)
{
...
QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData);
...
}
这里调用了QCoreApplicationPrivate的sendPostedEvents
来处理所有post来的事件。
QCoreApplicationPrivate事件处理
QCoreApplicationPrivate::sendPostedEvents
用来处理第三个参数所代表的线程的消息队列中的消息。
void QCoreApplicationPrivate::sendPostedEvents(QObject *receiver, int event_type,
QThreadData *data)
...
while (i < data->postEventList.size()) {
...
const QPostEvent &pe = data->postEventList.at(i);
...
QEvent *e = pe.event;
QObject * r = pe.receiver;
...
QCoreApplication::sendEvent(r, e)
...
}
这里调用QCoreApplication::sendEvent
将消息队列中的消息全部处理掉。
通常,我们想要同步处理一个事件,也可以直接调用sendEvent
,相当于不经过消息队列而是直接调用该事件的事件处理函数。
bool QCoreApplication::sendEvent(QObject *receiver, QEvent *event)
{
...
return notifyInternal2(receiver, event);
}
bool QCoreApplication::notifyInternal2(QObject *receiver, QEvent *event)
{
...
return self->notify(receiver, event);
}
sendEvent
实际调用了notify
来处理事件。
事件处理过程
到这一步开始,就涉及到具体事件的处理了。
QApplication的notify
可以这么说,每一个事件执行前,都要经过QApplication::notify
,所以,我们可以重写notify
来对事件做特殊处理。
bool QApplication::notify(QObject *receiver, QEvent *event)
{
...
switch (e->type()) {
...
case QEvent::Wheel: // User input and window activation makes tooltips sleep
case QEvent::ActivationChange:
case QEvent::KeyPress:
case QEvent::KeyRelease:
case QEvent::FocusOut:
case QEvent::FocusIn:
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::MouseButtonDblClick:
...
res = d->notify_helper(receiver, e);
...
}
处理事件过滤器
到这里调用链到了QCoreApplicationPrivate::notify_helper
,这个方法比较重要,我们来看主体代码:
bool QCoreApplicationPrivate::notify_helper(QObject *receiver, QEvent * event)
{
...
// send to all application event filters (only does anything in the main thread)
if (QCoreApplication::self
&& receiver->d_func()->threadData->thread.loadAcquire() == mainThread()
&& QCoreApplication::self->d_func()->sendThroughApplicationEventFilters(receiver, event)) {
filtered = true;
return filtered;
}
// send to all receiver event filters
if (sendThroughObjectEventFilters(receiver, event)) {
filtered = true;
return filtered;
}
// deliver the event
consumed = receiver->event(event);
return consumed;
}
可以看到,流程中涉及两个事件过滤器的调用:sendThroughApplicationEventFilters
和sendThroughObjectEventFilters
,事件过滤器调用完后,才是调用接收者的event
函数。
QApplication的事件过滤器
上一小节提到的sendThroughApplicationEventFilters
是处理app的事件过滤器。代码里会调用给app安装的所有事件过滤器(从代码中的注释看到,app的事件过滤器只能在主线程中被调用),我们给app安装的事件过滤器就是在这个阶段被执行的。
bool QCoreApplicationPrivate::sendThroughApplicationEventFilters(QObject *receiver, QEvent *event)
{
// We can't access the application event filters outside of the main thread (race conditions)
Q_ASSERT(receiver->d_func()->threadData->thread.loadAcquire() == mainThread());
if (extraData) {
// application event filters are only called for objects in the GUI thread
for (int i = 0; i < extraData->eventFilters.size(); ++i) {
QObject *obj = extraData->eventFilters.at(i);
...
if (obj->eventFilter(receiver, event))
return true;
}
}
return false;
}
QObject的事件过滤器
sendThroughObjectEventFilters
是处理对象的事件过滤器。里面会调用给接收者安装的全部事件过滤器,我们通过installEventFilter
给某个对象安装的事件过滤器就是在这个阶段执行的。
bool QCoreApplicationPrivate::sendThroughObjectEventFilters(QObject *receiver, QEvent *event)
{
...
for (int i = 0; i < receiver->d_func()->extraData->eventFilters.size(); ++i) {
QObject *obj = receiver->d_func()->extraData->eventFilters.at(i);
...
if (obj->eventFilter(receiver, event))
return true;
}
}
return false;
}
对象的event方法
从QCoreApplicationPrivate::notify_helper
代码可以看到,处理完事件接收者的事件过滤器后,就会调用接收者的event
方法来处理事件。
每一个从QObject继承出来的子类都有event方法,都可以处理自己的事件。
我们简单看下QWidget
的event
方法:
bool QWidget::event(QEvent *event)
{
...
switch (event->type()) {
...
case QEvent::MouseMove:
mouseMoveEvent((QMouseEvent*)event);
break;
case QEvent::MouseButtonPress:
mousePressEvent((QMouseEvent*)event);
break;
case QEvent::MouseButtonRelease:
mouseReleaseEvent((QMouseEvent*)event);
break;
case QEvent::MouseButtonDblClick:
mouseDoubleClickEvent((QMouseEvent*)event);
break;
...
}
到这里是不是很熟悉,各个具体的事件处理函数就是在这个阶段被调用的。
结尾
通过阅读源码,我们大概梳理了事件循环是如何开启的,以及事件的处理过程是怎么样的,也知道了为什么我们拦截事件或者特殊处理事件的顺序是QApplication::notify
->QApplication::installEventFilter
->QObject::installEventFilter
->QObject::event
。