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;
        ...
}

到这里是不是很熟悉,各个具体的事件处理函数就是在这个阶段被调用的。

猜你喜欢

转载自blog.csdn.net/u012794472/article/details/129265453