qt源码---事件系统之QCoreApplication

上一节分析了qt和windows系统之间的消息的传递,本节着重看一下,qt内部的事件是如何传递的?

1.sendEvent函数

在使用的自定义事件时,有时需要手动抛出一个事件,常用的方式有2种,其一时阻塞式的sendEvent函数;其二是postEvent函数;sendEvent函数定义如下:

其主要是将spont设置为fasle,然后转入到notifyInternal2函数中执行处理;

其中self在QCoreApplicationPrivate构造函数中已被指定,所以不为空,if(!self && selfRequired)不会执行;

QInternal::activateCallbacks函数,会检测是否注册了事件回调函数,如果注册了对应的事件函数,会截获此事件;其对应的定义如下:

 由于在实践项目中没有用过此种方式,此处略过

接下来会通过QScopedScopeLevelCounter类将接收对象的scopeLevel变量加1,并在函数退出时,减一;此处猜测是qt标识循环层级使用的,实际是做什么用的,还不清楚;

接下来会转入doNotifity函数处理,notify函数最终也是转入doNotify函数中处理;

此处会检测接收对象和发送对象是否在同一个线程;并最终转入notify_helper函数中处理;

 

 此处会判断接收的对象是否在主线程中,如果是在主线程中,会检测QCoreApplication的对象是否安装了事件过滤器,如果此事件被QCoreApplication事件过滤器检测到并被处理,并不会向下传递;

接下来会判断此接收对象上的事件过滤器是否处理此事件,如果此事件被处理则退出;

最后,进入接收对象自己的event函数,进行事件处理;

通过上述流程可以发现:1、event对象在事件传递过程中,没有执行删除等释放动作,其内存需要用户自己管理;2、sendEvent函数一直在执行函数的调用,并最终转到notify_helper函数中处理,所以其是阻塞式调用;3、事件的传递顺序,先被QCoreApplication对象拦截,接着被对象注册的事件过滤器拦截,最后执行自己的event函数;

2、postEvent函数

 

首先判断接收对象是否为空,如果为空,删除event对象,并退出;

判断接收对象的线程是否被释放,如果接收对象所在的线程已被删除,则删除event对象,并退出;

如果接收对象被移动到其他线程中,再次检测新的线程是否被删除,如果新的线程已被删除,则删除event对象,并退出;

判断事件是否可以被压缩,即删除已经在postEventList中重复的事件,主要针对QTimerEvent事件、QDeferredDeleteEvent、QQuitEvent等事件;

 接下来,执行:

 将QEvent对象放入QScopedPointer中,将receiver、event对象拼装成QPostEvent对象,并放入到QPostEventList中,如果中间出现异常,则event对象内存有QScopedPointer对象回收;

QPostEventList是根据priority优先级进行排序执行的;

接下来将event->posted置为true,postedEvents数量加1,并将canWait设置为false;

最后调用QAbstractEventDispatcher的wakeUp函数;wakeUp函数会调用WinAPI的PostMessage函数,投递一个WM_QT_SENDPOSTEDEVENTS事件,其会被processEvent函数捡取到,并最终转入QCoreApplicationPrivate中的sendPostedEvents函数处理(具体可以参考上一节);

 

 首先判断receiver对象是否为空,是否跨线程执行?由于通过QAbstractEventDispatcher对象调用,其receiver对象为空,所以此处会跳过执行;

将recursion加1,防止递归调用;并判断postEventList列表中是否有事件,如果没有事件需要处理则退出;

接下来主要是从QPostEventList中取出事件,并调用QCoreApplication::sendEvent函数处理;

并且会在此处删除event对象,由QScopedPointer对象负责处理;

另外qt在处理取事件的过程中非常小心,考虑了很多异常情况

期间定义了CleanUp的类,主要是用来处理事件对象被处理后,清空事件列表中已经处理过的事件;

总结:

sendEvent函数:

1、event对象在事件传递过程中,没有执行删除等释放动作,其内存需要用户自己管理;

2、sendEvent函数一直在执行函数的调用,并最终转到notify_helper函数中处理,所以其是阻塞式调用;

3、事件的传递顺序,先被QCoreApplication对象拦截,接着被对象注册的事件过滤器拦截,最后执行自己的event函数;

postEvnet函数:

1、event对象会被保存在全局的postEventList列表中,由事件在处理时被释放;

2、postevent函数会将事件、receiver对象放到全局的QPostEventList列表中,并通过WINAPI的postmessage系统函数,通知QAbstractEventDispatcher类检索到,最终转入到sendPostedEvents函数中处理;

3、上一节已提到windows消息及qt自定义消息如何被检索的过程,所以可以判断出postevent函数的处理是异步的过程;

QCoreApplication类的主要内容已基本结束,下一节会着重介绍下定时器事件;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Qt事件过滤器 Qt事件模型一个真正强大的特色是一个QObject 的实例能够管理另一个QObject 实例的事件。 让我们试着设想已经有了一个CustomerInfoDialog的小部件。CustomerInfoDialog 包含一系列QLineEdit. 现在,我们想用空格键来代替Tab,使焦点在这些QLineEdit间切换。 一个解决的方法是子类化QLineEdit,重新实现keyPressEvent(),并在keyPressEvent()里调用focusNextChild()。像下面这样: void MyLineEdit::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Space) { focusNextChild(); } else { QLineEdit::keyPressEvent(event); } } 但这有一个缺点。如果CustomerInfoDialog里有很多不同的控件(比如QComboBox,QEdit,QSpinBox),我们就必须子类化这么多控件。这是一个烦琐的任务。 一个更好的解决办法是: 让CustomerInfoDialog去管理他的子部件的按键事件,实现要求的行为。我们可以使用事件过滤器。 一个事件过滤器的安装需要下面2个步骤: 1, 调用installEventFilter()注册需要管理的对象。 2,在eventFilter() 里处理需要管理的对象的事件。 一般,推荐在CustomerInfoDialog的构造函数中注册被管理的对象。像下面这样: CustomerInfoDialog::CustomerInfoDialog(QWidget *parent) : QDialog(parent){ ... firstNameEdit->installEventFilter(this); lastNameEdit->installEventFilter(this); cityEdit->installEventFilter(this); phoneNumberEdit->installEventFilter(this); } 一旦,事件管理器被注册,发送到firstNameEdit,lastNameEdit,cityEdit,phoneNumberEdit的事件将首先发送到eventFilter()。 下面是一个 eventFilter()函数的实现: bool CustomerInfoDialog::eventFilter(QObject *target, QEvent *event) { if (target == firstNameEdit || target == lastNameEdit || target == cityEdit || target == phoneNumberEdit) { if (event->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event); if (keyEvent->key() == Qt::Key_Space) { focusNextChild(); return true; } } } return QDialog::eventFilter(target, event); } 在上面的函数中,我们首先检查目标部件是否是 firstNameEdit,lastNameEdit,cityEdit,phoneNumberEdit。接着,我们判断事件是否是按键事件。如果事件是按键事件,我们把事件转换为QKeyEvent。接着,我们判断是否按下了空格键,如果是,我们调用focusNextChild(),把焦点传递给下一个控件。然后,返回,true通知Qt,我们已经处理了该事件。 如果返回false的话,Qt继续将该事件发送给目标控件,结果是一个空格被插入到QLineEdit中。 如果目标控件不是 QLineEdit,或者按键不是空格键,我们将把事件传递给基类的eventFilter()函数。 Qt提供5个级别
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 的官方文档和源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值