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

 豆子  2012年10月23日  Qt 学习之路 2  21条评论

尽管 Qt 已经提供了很多事件,但对于更加千变万化的需求来说,有限的事件都是不够的。例如,我要支持一种新的设备,这个设备提供一种崭新的交互方式,那么,这种事件如何处理呢?所以,允许创建自己的事件 类型也就势在必行。即便是不说那种非常极端的例子,在多线程的程序中,自定义事件也是尤其有用。当然,事件也并不是局限在多线程中,它可以用在单线程的程序中,作为一种对象间通讯的机制。那么,为什么我需要使用事件,而不是信号槽呢?主要原因是,事件的分发既可以是同步的,又可以是异步的,而函数的调用或者说是槽的回调总是同步的。事件的另外一个好处是,它可以使用过滤器。

Qt 自定义事件很简单,同其它类库的使用很相似,都是要继承一个类进行扩展。在 Qt 中,你需要继承的类是QEvent

继承QEvent类,最重要的是提供一个QEvent::Type类型的参数,作为自定义事件的类型值。回忆一下,这个 type 是我们在处理事件时用于识别事件类型的代号。比如在event()函数中,我们使用QEvent::type()获得这个事件类型,然后与我们定义的实际类型对比。

QEvent::TypeQEvent定义的一个枚举。因此,我们可以传递一个 int 值。但是需要注意的是,我们的自定义事件类型不能和已经存在的 type 值重复,否则会有不可预料的错误发生。因为系统会将你新增加的事件当做系统事件进行派发和调用。在 Qt 中,系统保留 0 – 999 的值,也就是说,你的事件 type 要大于 999。这种数值当然非常难记,所以 Qt 定义了两个边界值:QEvent::UserQEvent::MaxUser。我们的自定义事件的 type 应该在这两个值的范围之间。其中,QEvent::User的值是 1000,QEvent::MaxUser的值是 65535。从这里知道,我们最多可以定义 64536 个事件。通过这两个枚举值,我们可以保证我们自己的事件类型不会覆盖系统定义的事件类型。但是,这样并不能保证自定义事件相互之间不会被覆盖。为了解决这个问题,Qt 提供了一个函数:registerEventType(),用于自定义事件的注册。该函数签名如下:

 
 
1
static int QEvent::registerEventType ( int hint = -1 );

这个函数是 static 的,因此可以使用QEvent类直接调用。函数接受一个 int 值,其默认值是 -1;函数返回值是向系统注册的新的 Type 类型的值。如果 hint 是合法的,也就是说这个 hint 不会发生任何覆盖(系统的以及其它自定义事件的),则会直接返回这个值;否则,系统会自动分配一个合法值并返回。因此,使用这个函数即可完成 type 值的指定。这个函数是线程安全的,不必另外添加同步。

我们可以在QEvent子类中添加自己的事件所需要的数据,然后进行事件的发送。Qt 中提供了两种事件发送方式:

  1.  
     
    1
    2
    static bool QCoreApplication::sendEvent(QObject *receiver,
                                            QEvent *event);

    直接将event事件发送给receiver接受者,使用的是QCoreApplication::notify()函数。函数返回值就是事件处理函数的返回值。在事件被发送的时候,event对象并不会被销毁。通常我们会在栈上创建event对象,例如:

     
     
    1
    2
    QMouseEvent event(QEvent::MouseButtonPress, pos, 0, 0, 0);
    QApplication::sendEvent(mainWindow, &event);
  2.  
     
    1
    2
    static void QCoreApplication::postEvent(QObject *receiver,
                                            QEvent *event);

    event事件及其接受者receiver一同追加到事件队列中,函数立即返回。

    因为 post 事件队列会持有事件对象,并且在其 post 的时候将其 delete 掉,因此,我们必须在堆上创建event对象。当对象被发送之后,再试图访问event对象就会出现问题(因为 post 之后,event对象就会被 delete)。

    当控制权返回到主线程循环时,保存在事件队列中的所有事件都通过notify()函数发送出去。

    事件会根据 post 的顺序进行处理。如果你想要改变事件的处理顺序,可以考虑为其指定一个优先级。默认的优先级是Qt::NormalEventPriority

    这个函数是线程安全的。

    Qt 还提供了一个函数:

     
     
    1
    2
    static void QCoreApplication::sendPostedEvents(QObject *receiver,
                                                   int event_type);

    这个函数的作用是,将事件队列中的接受者为receiver,事件类似为 event_type 的所有事件立即发送给 receiver 进行处理。需要注意的是,来自窗口系统的事件并不由这个函数进行处理,而是processEvent()。详细信息请参考 Qt API 手册。

现在,我们已经能够自定义事件对象,已经能够将事件发送出去,还剩下最后一步:处理自定义事件。处理自定义事件,同前面我们讲解的那些处理方法没有什么区别。我们可以重写QObject::customEvent()函数,该函数接收一个QEvent对象作为参数:

 
 
1
void QObject::customEvent(QEvent *event);

我们可以通过转换 event 对象类型来判断不同的事件:

 
 
1
2
3
4
void CustomWidget::customEvent(QEvent *event) {
    CustomEvent *customEvent = static_cast<CustomEvent *>(event);
    // ...
}

当然,我们也可以在event()函数中直接处理:

 
 
1
2
3
4
5
6
7
8
bool CustomWidget::event(QEvent *event) {
    if (event->type() == MyCustomEventType) {
        CustomEvent *myEvent = static_cast<CustomEvent *>(event);
        // processing...
        return true;
    }
    return QWidget::event(event);
}
scutlpf  2014年2月19日 

”事件的分发既可以是同步的,又可以是异步的,而函数的调用或者说是槽的回调总是同步的。“这句话怎么理解?

  • 豆子  2014年2月20日 

    同步就是调用者必须等到被调用者执行完毕之后才能继续执行;异步就是被调用者在被调用之后立刻返回给调用者,调用者继续执行。函数调用和槽的执行都是同步的,就是要等到被调用的函数或槽执行完毕,才返回给调用者继续执行,可以认为调用者被阻塞了。对于事件,使用 sendEvent 函数就是同步调用,使用 postEvent 就是异步调用。

 

Qt 学习之路 2(23):自定义事件的更多相关文章

  1. Qt 学习之路:线程和事件循环

    前面一章我们简单介绍了如何使用QThread实现线程.现在我们开始详细介绍如何“正确”编写多线程程序.我们这里的大部分内容来自于Qt的一篇Wiki文档,有兴趣的童鞋可以去看原文. 在介绍在以前,我们要 ...

  2. Qt 学习之路 2(45):模型

    Home / Qt 学习之路 2 / Qt 学习之路 2(45):模型 Qt 学习之路 2(45):模型  豆子  2013年2月26日  Qt 学习之路 2  23条评论 在前面两章的基础之上,我们 ...

  3. Qt 学习之路 2(37):文本文件读写

    Qt 学习之路 2(37):文本文件读写 豆子 2013年1月7日 Qt 学习之路 2 23条评论 上一章我们介绍了有关二进制文件的读写.二进制文件比较小巧,却不是人可读的格式.而文本文件是一种人可读 ...

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

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

  5. Qt 学习之路 2(18):事件

    Home / Qt 学习之路 2 / Qt 学习之路 2(18):事件 Qt 学习之路 2(18):事件  豆子  2012年9月27日  Qt 学习之路 2  60条评论 事件(event)是由系统 ...

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

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

  7. Qt 学习之路 2(19):事件的接受与忽略

    Home / Qt 学习之路 2 / Qt 学习之路 2(19):事件的接受与忽略 Qt 学习之路 2(19):事件的接受与忽略  豆子  2012年9月29日  Qt 学习之路 2  140条评论 ...

  8. Qt 学习之路 2(5):自定义信号槽

    Home / Qt 学习之路 2 / Qt 学习之路 2(5):自定义信号槽 Qt 学习之路 2(5):自定义信号槽  豆子  2012年8月24日  Qt 学习之路 2  131条评论 上一节我们详 ...

  9. Qt 学习之路 2(72):线程和事件循环

    Qt 学习之路 2(72):线程和事件循环 <理解不清晰,不透彻>  --  有需求的话还需要进行专题学习  豆子  2013年11月24日  Qt 学习之路 2  34条评论 前面一章我 ...

随机推荐

  1. 第四章 Java并发编程基础

    线程简介 什么是线程? 现代操作系统在一个运行程序时,会为其创建一个进程.例如,启动一个Java程序,操作系统就会创建一个Java进程.现代操作系统调度的最小单元是线程,也叫轻量进程(Light We ...

  2. dtgrid 手动条件删除表格中的某一行

    dtgrid 手动条件删除表格中的某一行 var grid = $.fn.DtGrid.init(dtGridOption); $(function () { grid.load(); }); fun ...

  3. 01-19asp.net基础--网站登录及验证

    第一步: 1)首先使用“CodeSmith”将Examinee类实体化,并生成实体类连接数据库的方法,存在解决方案下的“App_Code”文件夹下. 修改一下连接某个数据库: private SqlC ...

  4. AFNetworking-2.5-源码阅读剖析--网络请求篇

    一.前言 AFNetworking,非常友好简单的网络请求第三方框架,在GitHub中已经获得了25000++的star,链接地址:https://github.com/AFNetworking/AF ...

  5. 【262】pscp命令 实现windows与linux互传文件

    首先将pscp.exe文件放在某个文件夹中 新建*.bat文件 w-wx.bat代码 @echo off pscp.exe -pw l*****h D:\Windows-Linux\Data\* oc ...

  6. scp命令 跨服务器传输

    scp命令用于在Linux下进行远程拷贝文件的命令,和它类似的命令有cp,不过cp只是在本机进行拷贝不能跨服务器,而且scp传输是加密的.可能会稍微影响一下速度.当你服务器硬盘变为只读read onl ...

  7. 关于RAW 和 ASSEST文件夹的差异

    以下内容转自:http://www.cnblogs.com/leizhenzi/archive/2011/10/18/2216428.html *res/raw和assets的相同点: 1.两者目录下 ...

  8. 学习CSS的思路(转)

    两周之前写过该系列的第一篇,其实当时只是一个想法,这段时间迟迟未更新,是在思考一个解决过程.现在初有成效,就开更吧. 1. 一个段子 开题不必太严肃,写博客也不像写书,像聊天似的写东西是最好的表达方式 ...

  9. codeforces 1064D 双端队列BFS

    双端队列BFS解决的就是路径权值可能为0的图最短路问题,权值为0插入队头,否则插入队尾. 对于这个题,可以看作上下移动的路径的权值为0,左右移动权值为1,而且不能超过规定的步数. 直接广搜求覆盖的点的 ...

  10. sql如何选取两个数据表中的值

    一.直接在要选择的数据前面加上数据表的名字就行了 SELECT po.OrderID, p.LastName, p.FirstName FROM Persons AS p, Product_Order ...