找回密码
 立即注册
收起左侧

Qt学习之路第20篇 event()

8
回复
22321
查看
[复制链接]
累计签到:3 天
连续签到:1 天
来源: 2013-9-9 13:57:57 显示全部楼层 |阅读模式

马上注册,查看详细内容!注册请先查看:注册须知

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
版权声明

该文章原创于Qter开源社区(www.qter.org),作者devbean,博客www.devbean.net,转载请注明出处!



前面的章节中我们曾经提到 event() 函数。事件对象创建完毕后,Qt 将这个事件对象传递给 QObject 的 event() 函数。event() 函数并不直接处理事件,而是将这些事件对象按照它们不同的类型,分发给不同的事件处理器(event handler)。


如上所述,event() 函数主要用于事件的分发。所以,如果你希望在事件分发之前做一些操作,就可以重写这个 event() 函数了。例如,我们希望在一个 QWidget 组件中监听 tab 键的按下,那么就可以继承 QWidget,并重写它的 event() 函数,来达到这个目的:

  1. bool CustomWidget::event(QEvent *e)
  2. {
  3.     if (e->type() == QEvent::KeyPress) {
  4.         QKeyEvent *keyEvent = static_cast<QKeyEvent *>(e);
  5.         if (keyEvent->key() == Qt::Key_Tab) {
  6.             qDebug() << "You press tab.";
  7.             return true;
  8.         }
  9.     }
  10.     return QWidget::event(e);
  11. }
复制代码

CustomWidget 是一个普通的 QWidget 子类。我们重写了它的 event() 函数,这个函数有一个 QEvent 对象作为参数,也就是需要转发的事件对象。函数返回值是 bool 类型。如果传入的事件已被识别并且处理,则需要返回 true,否则返回 false。如果返回值是 true,并且,该事件对象设置了 accept(),那么 Qt 会认为这个事件已经处理完毕,不会再将这个事件发送给其它对象,而是会继续处理事件队列中的下一事件。注意,在 event() 函数中,调用事件对象的 accept() 和 ignore() 函数是没有作用的,不会影响到事件的传播。


我们可以通过使用 QEvent::type() 函数可以检查事件的实际类型,其返回值是 QEvent::Type 类型的枚举。我们处理过自己感兴趣的事件之后,可以直接返回 true,表示我们已经对此事件进行了处理;对于其它我们不关心的事件,则需要调用父类的 event() 函数继续转发,否则这个组件就只能处理我们定义的事件了。为了测试这一种情况,我们可以尝试下面的代码:

  1. bool CustomTextEdit::event(QEvent *e)
  2. {
  3.     if (e->type() == QEvent::KeyPress) {
  4.         QKeyEvent *keyEvent = static_cast<QKeyEvent *>(e);
  5.         if (keyEvent->key() == Qt::Key_Tab) {
  6.             qDebug() << "You press tab.";
  7.             return true;
  8.         }
  9.     }
  10.     return false;
  11. }
复制代码

CustomTextEdit 是 QTextEdit 的一个子类。我们重写了其 event() 函数,却没有调用父类的同名函数。这样,我们的组件就只能处理 Tab 键,再也无法输入任何文本,也不能响应其它事件,比如鼠标点击之后也不会有光标出现。这是因为我们只处理的 KeyPress 类型的事件,并且如果不是 KeyPress 事件,则直接返回 false,鼠标事件根本不会被转发,也就没有了鼠标事件。


通过查看 QObject::event() 的实现,我们可以理解,event() 函数同前面的章节中我们所说的事件处理器有什么联系:

  1. //!!! Qt5
  2. bool QObject::event(QEvent *e)
  3. {
  4.     switch (e->type()) {
  5.     case QEvent::Timer:
  6.         timerEvent((QTimerEvent*)e);
  7.         break;

  8.     case QEvent::ChildAdded:
  9.     case QEvent::ChildPolished:
  10.     case QEvent::ChildRemoved:
  11.         childEvent((QChildEvent*)e);
  12.         break;
  13.     // ...
  14.     default:
  15.         if (e->type() >= QEvent::User) {
  16.             customEvent(e);
  17.             break;
  18.         }
  19.         return false;
  20.     }
  21.     return true;
  22. }
复制代码

这是 Qt 5 中 QObject::event() 函数的源代码(Qt 4 的版本也是类似的)。我们可以看到,同前面我们所说的一样,Qt 也是使用 QEvent::type() 判断事件类型,然后调用了特定的事件处理器。比如,如果 event->type() 返回值是 QEvent::Timer,则调用 timerEvent() 函数。可以想象,QWidget::event() 中一定会有如下的代码:

  1. switch (event->type()) {
  2.     case QEvent::MouseMove:
  3.         mouseMoveEvent((QMouseEvent*)event);
  4.         break;
  5.     // ...
  6. }
复制代码

事实也的确如此。timerEvent() 和 mouseMoveEvent() 这样的函数,就是我们前面章节所说的事件处理器 event handler。也就是说,event() 函数中实际是通过事件处理器来响应一个具体的事件。这相当于 event() 函数将具体事件的处理“委托”给具体的事件处理器。而这些事件处理器是 protected virtual 的,因此,我们重写了某一个事件处理器,即可让 Qt 调用我们自己实现的版本。


由此可以见,event() 是一个集中处理不同类型的事件的地方。如果你不想重写一大堆事件处理器,就可以重写这个 event() 函数,通过 QEvent::type() 判断不同的事件。鉴于重写 event() 函数需要十分小心注意父类的同名函数的调用,一不留神就可能出现问题,所以一般还是建议只重写事件处理器(当然,也必须记得是不是应该调用父类的同名处理器)。这其实暗示了 event() 函数的另外一个作用:屏蔽掉某些不需要的事件处理器。正如我们前面的 CustomTextEdit 例子看到的那样,我们创建了一个只能响应 tab 键的组件。这种作用是重写事件处理器所不能实现的。






参与人数 7人气 +11 收起 理由
侯侯大神 + 2 对我帮助很大!
onlinepp + 1 看了很多遍 终于有点头目了
恒星shephered + 2 必须支持!
zyhan17 + 2 很实用!
gavin_8724 + 1
who_am_i_2015 + 2 必须支持!
daysMark + 1

查看全部评分总评分 : 人气 +11

回复

使用道具 举报

累计签到:6 天
连续签到:1 天
2015-9-9 15:49:13 显示全部楼层
本帖最后由 Aurora_1992 于 2015-9-9 15:53 编辑

原来委托是这么个意思。
event()就是一个把事件集中处理起来的地方,具体怎么处理,当switch选择后,用case下的处理器(也就是函数)来实现。

托付给别的人或机构办理
回复 支持 反对

使用道具 举报

累计签到:20 天
连续签到:1 天
2016-5-31 23:49:17 显示全部楼层
讲的很详细,把GUI中较核心的内容都讲明白了!学习当中
回复 支持 反对

使用道具 举报

累计签到:8 天
连续签到:1 天
2016-8-30 15:26:46 显示全部楼层
event(),就是中介呗,这个好懂
回复 支持 反对

使用道具 举报

累计签到:4 天
连续签到:1 天
2016-10-12 10:24:14 显示全部楼层
写的很详细!谢谢分享!
回复 支持 反对

使用道具 举报

累计签到:14 天
连续签到:1 天
2018-5-22 11:24:35 显示全部楼层
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(e);   //这个等号右边是类型转换吗@devbean    不是太明白  求知道的朋友给解答一下
回复 支持 反对

使用道具 举报

累计签到:11 天
连续签到:1 天
2018-9-16 09:42:00 显示全部楼层
本帖最后由 owenmsd9880 于 2018-9-16 09:51 编辑
onlinepp 发表于 2018-5-22 11:24
QKeyEvent *keyEvent = static_cast(e);   //这个等号右边是类型转换吗@devbean    不是太明白  求知道的朋 ...

百度一下或者去CSDN搜一搜博客吧
回复 支持 反对

使用道具 举报

尚未签到

2019-2-22 11:57:56 显示全部楼层
写的挺好的,很实用,可以帮助自己快速的了解QT的相关知识,大家可以参考学习
回复 支持 反对

使用道具 举报

累计签到:27 天
连续签到:3 天
2019-4-14 09:07:45 显示全部楼层
我是个新手  可能我理解的不对  但是我觉得 如下这段 写的有点问题 :::CustomWidget 是一个普通的 QWidget 子类。我们重写了它的 event() 函数,这个函数有一个 QEvent 对象作为参数,也就是需要转发的事件对象。函数返回值是 bool 类型。如果传入的事件已被识别并且处理,则需要返回 true,否则返回 false。如果返回值是 true,并且,该事件对象设置了 accept(),那么 Qt 会认为这个事件已经处理完毕,不会再将这个事件发送给其它对象,而是会继续处理事件队列中的下一事件。注意,在 event() 函数中,调用事件对象的 accept() 和 ignore() 函数是没有作用的,不会影响到事件的传播。  然后我们再去 查看 楼主帖子中的源码  bool QObject::event(QEvent *e)   :::我的问题是 Qevent事件对象并没有 在Object::event()函数中进行释放  那么我们在自定义的事件过滤器中调用accept()或者 ignore() 函数貌似应该有效果~   因为我们调用的 父类的Object::event()函数并没有释放 Qevent对象  ,~~ 另一种可能是在事件对象的传递过程中    事件对象被释放    ,这我也 无从得知(全靠猜)^_^      但是我们可以注意到 楼主的 自定义事件过滤器并没有 释放该 事件对象     另外我还有一点疑惑 就是一个对象的释放  或者一块内存的回收  其回收机制应该有一个判断该对象是否 被使用的标志  而我个人猜测   这个标志  accept() 和 ignore()  还有Event 的返回值  就刚好合适用与判断是否应该回收该事件对象  因为这几个标志总是在走完整个事件得传力流程后才会被设置   所以想说的意思就是我很迷惑 他的这个事件对象是在什么时候释放的 ~~我通过QT查看了下 Qwidget::event 的源码 我只找到了声明 并没有找到其定义(我是新手 说的不对  请勿见笑)
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

公告
可以关注我们的微信公众号yafeilinux_friends获取最新动态,或者加入QQ会员群进行交流:190741849、186601429(已满) 我知道了