QT之事件系统
- 1. 概述
- 2. 事件的传递
- 3. 事件类型
- 4. 事件处理与事件过滤
- 5. 自定义事件
- 5.1 Demo
- 6. 发送事件
- 7. 参考
1. 概述
在QT中,事件均派生自QEvent抽象类,事件可以由任何派生自QObject的子类实例接收和处理。它们与widget关联性极强。
2. 事件的传递
当事件发生时,QT 通过构造适当的 QEvent 子类实例来创建一个事件对象来表示它,并通过调用其 event() 函数将其传递给 QObject 的特定实例
event() 函数本身不处理事件;它根据传递的事件类型,它会调用该特定类型事件的事件处理程序,并根据事件是被接受还是被忽略来发送响应。
传递的顺序如下:
3. 事件类型
大多数事件类型都有特殊的类,特别是 QResizeEvent、QPaintEvent、QMouseEvent、QKeyEvent 和 QCloseEvent。 每个类都是 QEvent 的子类并添加特定于事件的函数。 例如,QResizeEvent 添加了 size() 和 oldSize() 以使小部件能够发现它们的尺寸是如何改变的。
每个事件都有一个关联的类型,在 QEvent::Type 中定义,这可以用作运行时类型信息的方便来源,以快速确定给定事件对象是从哪个子类构造的。
具体的事件类型可参考,官网介绍
4. 事件处理与事件过滤
常用的事件处理方式如下:
- 重写各种事件的虚函数
如paintEvent()、mousePressEvent()等。这是最常见的方式,同时也是最简单和功能最弱的方式 - 重写event()函数
event()函数是所有对象的事件入口,QObject和QWidget中的实现,默认是把事件传递给特定的事件处理函数 - 在特定对象上面安装事件过滤器
该过滤器仅过滤该对象接收到的事件。 - 在QCoreApplication::instance()上面安装事件过滤器
该过滤器将过滤所有对象的所有事件,因此和notify()函数一样强大,但是它更灵活,因为可以安装多个过滤器。全局的事件过滤器可以看到 disabled 组件上面发出的鼠标事件。全局过滤器有一个问题:只能用在主线程。 - 重写QCoreApplication::notify()函数
这是最强大的,和全局事件过滤器一样提供完全控制,并且不受线程的限制。但是全局范围内只能有一个被使用(因为QCoreApplication是单例的)
Qt 调用 QApplication 来发送一个事件,重新实现 notify()函数是在事件过滤器得到所有事件之前获得它们的唯一方法
。事件过滤器使用更为便利。因为可以同时有多个事件过滤器。而 notify()函数只有一个。
5. 自定义事件
QT
自定义事件的创建步骤如下:
- 继承QEvent类
- 定义一个事件类型的两种方式
QT
中有两个宏用来定义了用户事件的最大边界(QEvent::MaxUser
)和最小边界(QEvent::User
)- 向
QT
事件系统注册自定义事件类型
在上述步骤中可以创建出一个与系统事件类型不冲突的自定义事件类型,但是无法保证自定义事件类型不冲突。因此需要将自定义事件类型注册到QT
事件系统中,这样若有相同的类型再次被注册时QT
事件系统就可以处理自定义事件类型冲突的问题。注册函数如下:// 线程安全 // registerEventType 若传入参数可用则返回此参数,否则返回QEvent::MaxUser和QEvent::User之间的值,若参数不在QEvent::MaxUser和QEvent::User之间则忽略参数 static int QEvent::registerEventType ( int hint = -1 );
5.1 Demo
- myevent.h
#ifndef MYEVENT_H #define MYEVENT_H #include <QEvent> class MyEvent : public QEvent { public: MyEvent(); int getValue(); static Type eventType; private: int m_value; }; #endif // MYEVENT_H
- myevent.cpp
#include "myevent.h" QEvent::Type MyEvent::eventType = (QEvent::Type)QEvent::registerEventType(); MyEvent::MyEvent(): QEvent(eventType), m_value(1) { } int MyEvent::getValue() { return m_value; }
- mainwindow.h
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); virtual bool event(QEvent *event) override; protected: // 自定义事件的处理(专门处理自定义事件的),也可以用event()函数处理 virtual void customEvent(QEvent *event) override; private: Ui::MainWindow *ui; }; #endif // MAINWINDOW_H
- mainwindow.cpp
#include "mainwindow.h" #include "ui_mainwindow.h" #include "myevent.h" #include <QDebug> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); MyEvent* myEvent = new MyEvent; // 事件只能定义在堆上,不用手动释放,系统会自动释放 qApp->postEvent(this,myEvent); } MainWindow::~MainWindow() { delete ui; } bool MainWindow::event(QEvent *event) { QEvent::Type eventType = event->type(); if(eventType == MyEvent::eventType) { MyEvent* myEvent = dynamic_cast<MyEvent*>(event); qDebug() << "event" << eventType ; qDebug() << "event" << myEvent->getValue() ; event->ignore(); } // 处理完成后继续传播事件,这样customEvent就会收到自定义事件 return QObject::event(event); } void MainWindow::customEvent(QEvent *event) { QEvent::Type eventType = event->type(); if(eventType == MyEvent::eventType) { // 这里默认是true qDebug() << "customEvent" << event->isAccepted(); MyEvent* myEvent = dynamic_cast<MyEvent*>(event); qDebug() << "customEvent" << eventType ; qDebug() << "customEvent" << myEvent->getValue() ; //event->accept(); } }
event()处理事件的时机比customEvent()处理的早
6. 发送事件
- sendEvent()
sendEvent() 立即处理事件。 当它返回时,事件过滤器和/或对象本身已经处理了该事件。
对于许多事件类,有一个名为 isAccepted() 的函数可以告诉您事件是被调用的最后一个处理程序接受还是拒绝。 - postEvent()
postEvent() 将事件发布到队列中以供稍后分派。 下次 Qt 的主事件循环运行时,它会调度所有已发布的事件,并进行一些优化。 例如,如果有多个调整大小事件,它们将被压缩为一个。
这同样适用于绘制事件:QWidget::update() 调用 postEvent(),它通过避免多次重绘来消除闪烁并提高速度。
7. 参考
- QT事件处理–notify()
- https://blog.csdn.net/qq_40729688/article/details/89500394
- 官方事件系统介绍文档