Qt-事件循环概念及循环函数详解

55 篇文章 26 订阅

Qt事件循环详解
事件循环简介
QT事件循环过程
启动事件循环
QEventLoop事件循环
QEventDispatcherUNIX事件处理
QCoreApplicationPrivate事件处理
事件处理过程
QApplication的notify
处理事件过滤器
QApplication的事件过滤器
QObject的事件过滤器
对象的event方法
结尾
事件循环简介
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。

  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Qt的事件循环是一个非常重要的概念,也是Qt能够实现异步、响应式编程的关键所在。简单来说,事件循环是一个无限循环,用于处理Qt应用程序中的各种事件,包括用户输入、定时器事件、网络事件等等。以下是Qt事件循环的原理: 1. 事件队列:Qt应用程序中的所有事件都被放置在一个事件队列中,事件队列是一个先进先出的队列结构。 2. 事件循环:Qt应用程序启动后,会进入一个无限循环,也就是事件循环事件循环会不断地从事件队列中取出事件,并将事件分发给对应的对象进行处理。 3. 事件分发:当事件循环事件队列中取出一个事件时,会根据事件的类型和目标对象,将事件分发给对应的对象进行处理。如果事件是一个用户输入事件,那么它会被分发给当前获得焦点的控件进行处理;如果事件是一个定时器事件,那么它会被分发给对应的定时器对象进行处理。 4. 事件处理:一旦事件被分发给对应的对象,该对象就会调用自己的事件处理函数来处理事件。例如,一个按钮控件的事件处理函数会检测用户是否点击了该按钮,如果是,则执行与该按钮相关的操作。 5. 事件过滤器:Qt框架允许对象在事件处理之前拦截事件进行处理,这个过程称为事件过滤。事件过滤器可以用来处理一些全局事件,例如窗口关闭事件、应用程序退出事件等等。 总之,Qt的事件循环是一个非常灵活、高效的机制,可以保证Qt应用程序能够实现异步、响应式编程。开发者只需要关注对象的事件处理函数,无需关心事件循环的具体实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值