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派生的子类,包括QEventDispatcherUNIXQEventDispatcherWin32等,这里我们以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;
}

可以看到,流程中涉及两个事件过滤器的调用:sendThroughApplicationEventFilterssendThroughObjectEventFilters,事件过滤器调用完后,才是调用接收者的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方法,都可以处理自己的事件。
我们简单看下QWidgetevent方法:

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

  • 13
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Qt事件循环是一个非常重要的机制,它负责接收和分发事件,保证 Qt 应用程序的正常运行。下面是简单的 Qt 事件循环源码分析: 1. Qt事件循环是通过 `QCoreApplication::exec()` 方法启动的。该方法首先会创建一个 `QEventLoop` 对象,然后进入一个无限循环。 2. 在事件循环中,`QEventLoop` 对象通过调用 `QCoreApplication::processEvents()` 方法来处理当前队列中的事件。该方法会检查是否有待处理的事件,如果没有,则线程会进入休眠状态,等待新的事件到来。 3. 当一个事件到来时,Qt 会根据事件的类型和目标对象,将事件分发给正确的接收者进行处理。接收者可以是窗口部件、控件、布局等。 4. 对于每个事件,Qt 会调用接收者的对应方法来处理。例如,对于鼠标点击事件,Qt 会调用接收者的 `mousePressEvent()` 方法来处理。 5. 在事件处理过程中,如果需要进行其他操作(如更新界面、执行定时器等),Qt 会将这些操作添加到事件队列中。 6. 当所有待处理的事件都被处理完毕后,Qt 会通过调用 `QCoreApplication::quit()` 方法退出事件循环,程序结束运行。 需要注意的是,Qt事件循环并不是单线程的。在多线程环境下,每个线程都可以有自己的事件循环,但每个线程只能有一个事件循环。当一个事件需要跨线程传递时,Qt 会通过事件队列和线程间的信号槽机制来实现。 以上是简单的 Qt 事件循环源码分析,如果您对具体的源码细节有更深入的需求,建议参考 Qt 的官方文档和源代码。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值