Qt 学习之路 2(20):event()

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

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

 
 
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);
}
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()函数继续转发,否则这个组件就只能处理我们定义的事件了。为了测试这一种情况,我们可以尝试下面的代码:

 
 
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;
}
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;
}

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

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

 
 
//!!! 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;
}

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()中一定会有如下的代码:

 
 
switch (event->type()) {
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()的更多相关文章

  1. Qt 学习之路 2(6):Qt 模块简介

    Home / Qt 学习之路 2 / Qt 学习之路 2(6):Qt 模块简介  豆子  2012年8月26日  Qt 学习之路 2  20条评论 Qt 5 与 Qt 4 最大的一个区别之一是底层架构 ...

  2. Qt 学习之路 2(36):二进制文件读写

    Qt 学习之路 2(36):二进制文件读写 豆子 2013年1月6日 Qt 学习之路 2 20条评论 在上一章中,我们介绍了有关QFile和QFileInfo两个类的使用.我们提到,QIODevice ...

  3. Qt 学习之路 2(54):剪贴板

    Qt 学习之路 2(54):剪贴板 豆子 2013年6月8日 Qt 学习之路 2 2条评论 剪贴板的操作经常和前面所说的拖放技术在一起使用.大家对剪贴板都很熟悉.我们可以简单地把它理解成一个数据存储池 ...

  4. Qt 学习之路 2(53):自定义拖放数据

    Qt 学习之路 2(53):自定义拖放数据 豆子  2013年5月26日  Qt 学习之路 2  13条评论上一章中,我们的例子使用系统提供的拖放对象QMimeData进行拖放数据的存储.比如使用QM ...

  5. Qt 学习之路 2(52):使用拖放

    Qt 学习之路 2(52):使用拖放 豆子 2013年5月21日 Qt 学习之路 2 17条评论 拖放(Drag and Drop),通常会简称为 DnD,是现代软件开发中必不可少的一项技术.它提供了 ...

  6. Qt 学习之路 2(33):贪吃蛇游戏(3)

    Qt 学习之路 2(33):贪吃蛇游戏(3) 豆子 2012年12月29日 Qt 学习之路 2 16条评论 继续前面一章的内容.上次我们讲完了有关蛇的静态部分,也就是绘制部分.现在,我们开始添加游戏控 ...

  7. Qt 学习之路 2(23):自定义事件

    Qt 学习之路 2(23):自定义事件  豆子  2012年10月23日  Qt 学习之路 2  21条评论 尽管 Qt 已经提供了很多事件,但对于更加千变万化的需求来说,有限的事件都是不够的.例如, ...

  8. Qt 学习之路 2(22):事件总结

    Qt 学习之路 2(22):事件总结 豆子 2012年10月16日 Qt 学习之路 2 47条评论 Qt 的事件是整个 Qt 框架的核心机制之一,也比较复杂.说它复杂,更多是因为它涉及到的函数众多,而 ...

  9. Qt 学习之路 2(21):事件过滤器

    Qt 学习之路 2(21):事件过滤器 豆子 2012年10月15日 Qt 学习之路 2 37条评论 有时候,对象需要查看.甚至要拦截发送到另外对象的事件.例如,对话框可能想要拦截按键事件,不让别的组 ...

随机推荐

  1. NBU客户端备份失败,报错error 48 client hostname could not be found

    今天在做备份时发现了这个报错.经过ping, nslookup, bpclntcmd命令检查没有发现连接或域名解析存在问题. 参考文档http://www.symantec.com/docs/TECH ...

  2. jdbcTemplate学习(二)

    前面讲了增加.删除.更新操作,这节讲一下查询. 查询操作: (一)查询一个值(不需要注入参数) queryForObject(String sql, Class<T> requiredTy ...

  3. 解决Xcode在debug时不在断点处停止的方法<转>

    搞了老半天不知道为什么 后来查了一下才解决问题,多谢原创作者的贡献. 新年后的第一发! -------------------------------- 前几天在开发的时候,Xcode设置断点后依然无 ...

  4. asp.net的验证码插件及方法、ashx验证码一般处理程序

    需要引入一个ashx的一般处理程序! 把这个程序在前台当作一个图片使用就可以! 前台代码: <td> <img title="看不清?" style=" ...

  5. Hbuilder实用技巧(转)

    Hbuilder实用技巧 原创 2016年05月19日 10:25:42 标签: hbuilder 操作 16551 1. Q:怎么实现代码追踪? A:在编辑代码时经常会出现需要跳转到引用文件或者变量 ...

  6. 在html中打开PDF

    <object classid="clsid:CA8A9780-280D-11CF-A24D-444553540000" width="990" heig ...

  7. c#基础 函数

    函数  就是方法 独立完成某项功能的一个个体 函数的的三个好处:1.提高代码的重用性.2.提高功能开发的效率,3.提高代码的可维护性. 函数也分为     固定功能函数, 高度抽象函数.高度抽象函数应 ...

  8. c#循环语句 for 循环嵌套的练习。还有跳转语句,异常语句,迭代穷举介绍

    先说一下循环嵌套:循环嵌套就是再一个循环里面再放一个循环,也就是说如果没一个循环都循环10次,那么第一个循环是1的时候,嵌套的循环会循环十次.也就是10*10的效果. for 循环语句 主要还是逻辑思 ...

  9. win10 更换秘钥报错:拒绝访问:所请求的操作需要提升特权

    直接打开cmd执行换秘钥的命令: slmgr /ipk VK7JG-NPHTM-C97JM-9MPGT-3V66T slmgr /skms kms.xspace.in slmgr /ato 报错如图: ...

  10. hive与hbase数据类型对应关系

    hive与hbase数据类型对应关系 当hbase中double,int 类型以byte方式存储时,用字符串取出来必然是乱码. 在hivd与hbase整合时也遇到这个问题:做法是:#b 1.加#b C ...