【QT】自定义事件

一、自定义事件的优势

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

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

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

二、自定义事件的Type 与 注册

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

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

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

三、 自定义事件的发送

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

  1. static bool QCoreApplication::sendEvent(QObject *receiver, QEvent *event);
    直接将event事件发送给receiver接受者,使用的是QCoreApplication::notify()函数。函数返回值就是事件处理函数的返回值。在事件被发送的时候,event对象并不会被销毁。通常我们会在栈上创建event对象,例如:
QMouseEvent event(QEvent::MouseButtonPress, pos, 0, 0, 0);
QApplication::sendEvent(mainWindow, &event);
  1. static void QCoreApplication::postEvent(QObject *receiver, QEvent *event);
    将event事件及其接受者receiver一同追加到事件队列中,函数立即返回。

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

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

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

    这个函数是线程安全的。

  2. Qt 还提供了一个函数:

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

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

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

    void QObject::customEvent(QEvent *event);
    

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

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

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

    bool CustomWidget::event(QEvent *event) {if (event->type() == MyCustomEventType) {CustomEvent *myEvent = static_cast<CustomEvent *>(event);// processing...return true;}return QWidget::event(event);
    }
    

QT 允许用户自定义event,自QT4开始,直接继承自QEvent
如果在线程中接收来自主线程的event,那么receiver GObject必须在线程的> run函数中创建
否则会导致接收不到event的情况发生。

四、示例代码

  • widget.h
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include <QEvent>
#include <QPushButton>
#include <QLabel>
#include <QGridLayout>
#include <QComboBox>
#include <QLineEdit>
#include <QTextEdit>class Widget : public QWidget
{Q_OBJECTpublic:explicit Widget(QWidget *parent = nullptr);~Widget();protected:void customEvent(QEvent *event);bool eventFilter(QObject *obj, QEvent *event);
private slots:void slotsystem();void slotusr();
private:QLabel* m_systemlab;QLabel* m_userlab;QPushButton* m_systembtn;QPushButton* m_userbtn;QComboBox* m_systembox;QLineEdit* m_useredit;QTextEdit* m_textedit;
};//定义自定义事件ID
const int userId = QEvent::User;
const int MyEventId = userId+6;//继承QEvent创建自定义事件类,注意初始化向量表时需要初始化事件ID。
class MyEvent :public QEvent
{public:MyEvent(QObject*parent,QString val);QString m_val;QObject* m_parent;
};#endif // WIDGET_H
  • widget.cpp
#include "widget.h"
#include <QApplication>
#include <QDebug>Widget::Widget(QWidget *parent) :QWidget(parent)
{m_systemlab = new QLabel("系统事件:",this);m_userlab = new QLabel("用户事件:",this);m_systembox = new QComboBox(this);m_useredit = new QLineEdit("0",this);m_systembtn = new QPushButton("发送",this);m_userbtn = new QPushButton("发送",this);m_textedit = new QTextEdit(this);m_systembox->addItems(QStringList()<<"Hide"<<"Show"<<"Close");QGridLayout* lay = new QGridLayout(this);lay->addWidget(m_systemlab,0,0,1,1);lay->addWidget(m_systembox,0,1,1,1);lay->addWidget(m_systembtn,0,2,1,1);lay->addWidget(m_userlab,1,0,1,1);lay->addWidget(m_useredit,1,1,1,1);lay->addWidget(m_userbtn,1,2,1,1);lay->addWidget(m_textedit,2,0,5,3);this->setLayout(lay);this->installEventFilter(this);connect(m_systembtn,SIGNAL(clicked()),this,SLOT(slotsystem()));connect(m_userbtn,SIGNAL(clicked()),this,SLOT(slotusr()));
}Widget::~Widget()
{}//发送系统事件
void Widget::slotsystem()
{#if 1//1if (m_systembox->currentText() == "Hide"){QEvent *e = new QEvent(QEvent::Hide);QApplication::postEvent(this,e);}else if(m_systembox->currentText() == "Show"){QEvent *e = new QEvent(QEvent::Show);QApplication::postEvent(this,e);}else if(m_systembox->currentText() == "Close"){QEvent *e = new QEvent(QEvent::Close);QApplication::postEvent(this,e);}
#else//2if (m_systembox->currentText() == "Hide"){QEvent e(QEvent::Hide);QApplication::sendEvent(this,&e);}else if (m_systembox->currentText() == "Show"){QEvent e(QEvent::Show);QApplication::sendEvent(this,&e);}else if (m_systembox->currentText() == "Close"){QEvent e(QEvent::Close);QApplication::sendEvent(this,&e);}
#endif
}//发送自定义事件
void Widget::slotusr()
{#if 0//1MyEvent* myevent = new MyEvent(this,m_useredit->text());QApplication::postEvent(this,myevent);
#else//2MyEvent myevent(this,m_useredit->text());QApplication::sendEvent(this,&myevent);
#endif
}//只能接收自定义事件
void Widget::customEvent(QEvent *event)
{if (event->type() == MyEventId){MyEvent* e = dynamic_cast<MyEvent*>(event);if (e){m_textedit->append("customEvent接收事件:自定义事件:参数:"+e->m_val);}}return QWidget::customEvent(event);
}//接收自定义事件和系统事件
bool Widget::eventFilter(QObject* obj,QEvent* event)
{if (event->type() == QEvent::Hide){m_textedit->append("eventFilter接收事件:系统事件:参数:Hide");}else if (event->type() == QEvent::Show){m_textedit->append("eventFilter接收事件:系统事件:参数:Show");}else if (event->type() == QEvent::Close){m_textedit->append("eventFilter接收事件:系统事件:参数:Close");}if (event->type() == MyEventId){MyEvent* e = dynamic_cast<MyEvent*>(event);if (e){m_textedit->append("eventFilter接收事件:自定义事件:参数:"+e->m_val);}}
}//--------------------------MyEvent-----注意初始化向量表时需要初始化事件ID。
MyEvent::MyEvent(QObject *parent, QString val):QEvent(QEvent::Type(MyEventId)),m_parent(parent),m_val(val)
{}

【QT】自定义事件 QCustomEvent相关推荐

  1. Qt 自定义事件的实现

    初学Qt,用了Qt自带的事件,然后想怎么才能定义自己的事件呢?又如何使用自定义事件呢?看了篇文章,说先要子类化QEvent,然后定义自己的QEvent::Type,然后重写QWidget::event ...

  2. Qt自定义事件实现及子线程向主线程传送事件消息

    近期在又一次学习Qt的时候,由于要涉及到子线程与主线程传递消息,所以便琢磨了一下.顺便把有用的记录下来,方便自己以后查询及各位同仁的參考! 特此声明,本篇博文主要讲述有用的,也就是直接说明怎么实现,就 ...

  3. Qt线程间通信-自定义事件

    线程间通信: 1.自定义事件:postEvent(): 2.信号与槽[本质也是通过自定义事件实现的] 注意:每个线程都有自己的事件循环 自定义事件: 1.从QEvent继承: 2.registerEv ...

  4. Qt 自定义信号与槽

    注 对象与槽理解 //第一个参数lineEdit是激发事件对象,信号中的方法必须在对象中存在,并在对象类头文件signals下定义,//第二个参数信号,//第三个参数this是槽方法所属类的对象,且必 ...

  5. QT中事件发送函数sendEvent()、postEvent()详解

    Qt发送事件分为两种 -阻塞型事件发送 需要重写接收对象的event()事件处理函数 当事件发送后,将会立即进入event()事件处理函数进行事件处理 通过sendEvent()静态函数实现阻塞发送: ...

  6. QT的事件分发、事件过滤器详解

    一.事件的流向 QT的各种控件(QObject的子类)都有事件处理成员函数,例如: bool QObject::event(QEvent *e);//所有事件 dragEnterEvent(QDrag ...

  7. Qt中事件循环机制详解

    问题1:Qt中常见的事件有哪些? 答:鼠标事件(QMouseEvent).键盘事件(QKeyEvent).绘制事件(QPaintEvent).窗口尺寸改变(QResizeEvent).滚动事件(QSc ...

  8. Qt 自定义标题栏,最小化、最大化、关闭窗口,双击最大化,鼠标拖动等效果实现

    文章目录 前言 效果 代码 .pro文件 widget.h widget.cpp widget.ui title.h title.cpp title.ui 前言 本次实验内容为Qt自定义标题栏,最小化 ...

  9. QT 自定义分页控件

    Qt 自定义页码控件 一.效果展示 二.头文件 #ifndef PAGECONTROL_H #define PAGECONTROL_H #include <QList> #include ...

最新文章

  1. STM32F4 HAL库开发 -- RTC
  2. 冷静处理因为一时疏忽产生的错误。是提升自己的重要方法
  3. 学术与技术(与队长交流偶得)
  4. PostgreSQL安装和简单配置
  5. day17 Java的object中常见的方法以及内部类
  6. 北信源管理网页卸载密码_怎么卸载找不到程序的流氓软件?
  7. caffe镜像︱window10+docker+ubuntu14.04+caffe镜像的简单尝试
  8. chrome插件介绍
  9. Matlab-Simulink文件类型总结
  10. 本科毕设不通过是什么原因,哪个少年不曾为如何能够顺利优秀毕业而愁眉苦战
  11. precede和previous_描写过去的词语 英语
  12. 计算机一级考试图片水印怎么加,图片水印怎么添加?一起来看看这几个方法
  13. Java调用支付宝身份认证接口
  14. 【论文笔记】DR-TANet: Dynamic Receptive Temporal Attention Network for Street Scene Change Detection
  15. vue项目引入字体.ttf
  16. 和绅的女人们——汉人、黑人、白人、还有他的“干”女儿
  17. 计算机专业-找工作相关经验
  18. ZooKeeper设置ACL权限控制--增加访问ip白名单
  19. lm358集成电路参数资料 原厂资料 pdf datasheet 免费下载
  20. 旅游规划pta(无向图求顶点间最短路径)

热门文章

  1. 小案例:用Python制作音乐播放器
  2. oracle环境变量配置教程,oracle_window中oracle环境变量设置方法分享,window server中Oracle的环境变量设 - phpStudy...
  3. ubuntu中非常好用的文件比较工具meld
  4. 5G通信系统应用场景与消费物联网、工业物联网应用场景综合分析
  5. 朴素的矩阵行列式计算之C++实现
  6. 2017中国互联网大会正在召开
  7. hbuildx打包成apk_HBuilderX生成本地打包App资源
  8. 创业的关键:顺势而为
  9. 今天的仿小米二级菜单显示来了 -----js
  10. 如何监听页面 DOM 变动并高效响应