Qt 学习之路 2(20):event()
Qt 学习之路 2(20):event()
前面的章节中我们曾经提到event()
函数。事件对象创建完毕后,Qt 将这个事件对象传递给QObject
的event()
函数。event()
函数并不直接处理事件,而是将这些事件对象按照它们不同的类型,分发给不同的事件处理器(event handler)。
如上所述,event()
函数主要用于事件的分发。所以,如果你希望在事件分发之前做一些操作,就可以重写这个event()
函数了。例如,我们希望在一个QWidget
组件中监听 tab 键的按下,那么就可以继承QWidget
,并重写它的event()
函数,来达到这个目的:
{
if (e->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(e);
if (keyEvent->key() == Qt::Key_Tab) {
qDebug() << "You press tab.";
return true;
}
}
return QWidget::event(e);
}
1
2
3
4
5
6
7
8
9
10
11
|
bool CustomWidget::event(QEvent *e)
{
if (e->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(e);
if (keyEvent->key() == Qt::Key_Tab) {
qDebug() << "You press tab.";
return true;
}
}
return QWidget::event(e);
}
|
CustomWidget
是一个普通的QWidget
子类。我们重写了它的event()
函数,这个函数有一个QEvent
对象作为参数,也就是需要转发的事件对象。函数返回值是 bool 类型。如果传入的事件已被识别并且处理,则需要返回 true,否则返回 false。如果返回值是 true,并且,该事件对象设置了accept()
,那么 Qt 会认为这个事件已经处理完毕,不会再将这个事件发送给其它对象,而是会继续处理事件队列中的下一事件。注意,在event()
函数中,调用事件对象的accept()
和ignore()
函数是没有作用的,不会影响到事件的传播。
我们可以通过使用QEvent::type()
函数可以检查事件的实际类型,其返回值是QEvent::Type
类型的枚举。我们处理过自己感兴趣的事件之后,可以直接返回 true,表示我们已经对此事件进行了处理;对于其它我们不关心的事件,则需要调用父类的event()
函数继续转发,否则这个组件就只能处理我们定义的事件了。为了测试这一种情况,我们可以尝试下面的代码:
{
if (e->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(e);
if (keyEvent->key() == Qt::Key_Tab) {
qDebug() << "You press tab.";
return true;
}
}
return false;
}
1
2
3
4
5
6
7
8
9
10
11
|
bool CustomTextEdit::event(QEvent *e)
{
if (e->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(e);
if (keyEvent->key() == Qt::Key_Tab) {
qDebug() << "You press tab.";
return true;
}
}
return false;
}
|
CustomTextEdit
是QTextEdit
的一个子类。我们重写了其event()
函数,却没有调用父类的同名函数。这样,我们的组件就只能处理 Tab 键,再也无法输入任何文本,也不能响应其它事件,比如鼠标点击之后也不会有光标出现。这是因为我们只处理的KeyPress
类型的事件,并且如果不是KeyPress
事件,则直接返回 false,鼠标事件根本不会被转发,也就没有了鼠标事件。
通过查看QObject::event()
的实现,我们可以理解,event()
函数同前面的章节中我们所说的事件处理器有什么联系:
bool QObject::event(QEvent *e)
{
switch (e->type()) {
case QEvent::Timer:
timerEvent((QTimerEvent*)e);
break;
case QEvent::ChildAdded:
case QEvent::ChildPolished:
case QEvent::ChildRemoved:
childEvent((QChildEvent*)e);
break;
// ...
default:
if (e->type() >= QEvent::User) {
customEvent(e);
break;
}
return false;
}
return true;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
//!!! Qt5
bool QObject::event(QEvent *e)
{
switch (e->type()) {
case QEvent::Timer:
timerEvent((QTimerEvent*)e);
break;
case QEvent::ChildAdded:
case QEvent::ChildPolished:
case QEvent::ChildRemoved:
childEvent((QChildEvent*)e);
break;
// ...
default:
if (e->type() >= QEvent::User) {
customEvent(e);
break;
}
return false;
}
return true;
}
|
这是 Qt 5 中QObject::event()
函数的源代码(Qt 4 的版本也是类似的)。我们可以看到,同前面我们所说的一样,Qt 也是使用QEvent::type()
判断事件类型,然后调用了特定的事件处理器。比如,如果event->type()
返回值是QEvent::Timer
,则调用timerEvent()
函数。可以想象,QWidget::event()
中一定会有如下的代码:
case QEvent::MouseMove:
mouseMoveEvent((QMouseEvent*)event);
break;
// ...
}
1
2
3
4
5
6
|
switch (event->type()) {
case QEvent::MouseMove:
mouseMoveEvent((QMouseEvent*)event);
break;
// ...
}
|
事实也的确如此。timerEvent()
和mouseMoveEvent()
这样的函数,就是我们前面章节所说的事件处理器 event handler。也就是说,event()
函数中实际是通过事件处理器来响应一个具体的事件。这相当于event()
函数将具体事件的处理“委托”给具体的事件处理器。而这些事件处理器是 protected virtual 的,因此,我们重写了某一个事件处理器,即可让 Qt 调用我们自己实现的版本。
由此可以见,event()
是一个集中处理不同类型的事件的地方。如果你不想重写一大堆事件处理器,就可以重写这个event()
函数,通过QEvent::type()
判断不同的事件。鉴于重写event()
函数需要十分小心注意父类的同名函数的调用,一不留神就可能出现问题,所以一般还是建议只重写事件处理器(当然,也必须记得是不是应该调用父类的同名处理器)。这其实暗示了event()
函数的另外一个作用:屏蔽掉某些不需要的事件处理器。正如我们前面的CustomTextEdit
例子看到的那样,我们创建了一个只能响应 tab 键的组件。这种作用是重写事件处理器所不能实现的。
okgogo注: 事件产生 -- 加入-- 事件队列 --取出事件 -- event() 函数 分发事件给事件处理器 -- Event Handler(是指是事件处理函数) 处理事件
Qt 学习之路 2(20):event()的更多相关文章
- Qt 学习之路 2(6):Qt 模块简介
Home / Qt 学习之路 2 / Qt 学习之路 2(6):Qt 模块简介 豆子 2012年8月26日 Qt 学习之路 2 20条评论 Qt 5 与 Qt 4 最大的一个区别之一是底层架构 ...
- Qt 学习之路 2(36):二进制文件读写
Qt 学习之路 2(36):二进制文件读写 豆子 2013年1月6日 Qt 学习之路 2 20条评论 在上一章中,我们介绍了有关QFile和QFileInfo两个类的使用.我们提到,QIODevice ...
- Qt 学习之路 2(54):剪贴板
Qt 学习之路 2(54):剪贴板 豆子 2013年6月8日 Qt 学习之路 2 2条评论 剪贴板的操作经常和前面所说的拖放技术在一起使用.大家对剪贴板都很熟悉.我们可以简单地把它理解成一个数据存储池 ...
- Qt 学习之路 2(53):自定义拖放数据
Qt 学习之路 2(53):自定义拖放数据 豆子 2013年5月26日 Qt 学习之路 2 13条评论上一章中,我们的例子使用系统提供的拖放对象QMimeData进行拖放数据的存储.比如使用QM ...
- Qt 学习之路 2(52):使用拖放
Qt 学习之路 2(52):使用拖放 豆子 2013年5月21日 Qt 学习之路 2 17条评论 拖放(Drag and Drop),通常会简称为 DnD,是现代软件开发中必不可少的一项技术.它提供了 ...
- Qt 学习之路 2(33):贪吃蛇游戏(3)
Qt 学习之路 2(33):贪吃蛇游戏(3) 豆子 2012年12月29日 Qt 学习之路 2 16条评论 继续前面一章的内容.上次我们讲完了有关蛇的静态部分,也就是绘制部分.现在,我们开始添加游戏控 ...
- Qt 学习之路 2(23):自定义事件
Qt 学习之路 2(23):自定义事件 豆子 2012年10月23日 Qt 学习之路 2 21条评论 尽管 Qt 已经提供了很多事件,但对于更加千变万化的需求来说,有限的事件都是不够的.例如, ...
- Qt 学习之路 2(22):事件总结
Qt 学习之路 2(22):事件总结 豆子 2012年10月16日 Qt 学习之路 2 47条评论 Qt 的事件是整个 Qt 框架的核心机制之一,也比较复杂.说它复杂,更多是因为它涉及到的函数众多,而 ...
- Qt 学习之路 2(21):事件过滤器
Qt 学习之路 2(21):事件过滤器 豆子 2012年10月15日 Qt 学习之路 2 37条评论 有时候,对象需要查看.甚至要拦截发送到另外对象的事件.例如,对话框可能想要拦截按键事件,不让别的组 ...
随机推荐
- CentOS 7.2 部署Rsync + Lsyncd服务实现文件实时同步/备份 (二)
发送端配置: 一.配置密钥 1. 主/从服务器之间启用基于密钥的身份验证.登录发送端服务器并用 " ssh-keygen " 命令生成公共或私有的密钥. 2. 使用 " ...
- java反射专题二
一丶Class中常用方法详解 1)getFields() 只能获取到运行时类中及其父类中声明为public的属性 2)getDeclaredFields() 获取运行时类本身声明的所有属性 3)get ...
- 学习计划Python-转载
作者:闲谈后链接:https://www.zhihu.com/question/29775447/answer/145395619来源:知乎著作权归作者所有,转载请联系作者获得授权. 不过需要说明的是 ...
- 《Android应用性能优化》 第6章 性能评测和剖析
1.时间测量 System.currentTimeMillis 精读和准确度可能不够:更改系统时间会影响结果:UTC时间1970/1/1 00:00:00到现在的毫秒数 System.nanoTime ...
- springmvc 初始化参数绑定(使用属性编辑器) 来处理类型转换问题
处理一种日期格式 处理器中的写法: index.jsp中的写法: 处理多种日期格式: 处理器的写法: 自定义的属性编辑器: index.jsp的写法:
- round四舍五入
#!/usr/bin/env python r = round(3.6) #四舍五入 print(r) C:\Python35\python3.exe F:/Python/2day/c7.py 4 P ...
- PL/SQL批处理语句(一)BULK COLLECT
我们知道PL/SQL程序中运行SQL语句是存在开销的,因为SQL语句是要提交给SQL引擎处理,这种在PL/SQL引擎和SQL引擎之间的控制转移叫做上下文却换,每次却换时,都有额外的开销.然而,FORA ...
- GBK、GB2312和UTF-8编码区分
GBK包含全部中文字符, GBK的文字编码是双字节来表示的,即不论中.英文字符均使用双字节来表示,只不过为区分中文,将其最高位都定成1. 至于UTF-8编码则是用以解决国际上字符的一种多字节编码,它对 ...
- Swing绘图机制
------------------siwuxie095 工程名:TestSwingPaintMethod 包名:com.siwuxie095.swin ...
- Spring第五天
1. [简答题]:简述一下hibernate和spring框架的整合步骤: 答: 1.加入hibernate jar包 2.编写持久化类 3.添加Hibernate的配置文件:hibernate.cf ...