Qt篇: 自定义事件
在Qt框架中虽然提供了非常多的事件对象,但是在项目开发中,开发新功能或者自定义新组件时,自定义事件是至关重要的,能够掌握自定义事件对象本质,能够轻松驾驭各种功能的扩展和新自定义组件开发。
事件发送
Qt允许在程序中自主的发送事件,事件发送分为两种类型:阻塞型事件发送和非阻塞型事件发送,在QApplication中分别提供了这两种事件发送类型的静态函数
- 阻塞型事件发送:事件发送后需要等待事件处理完成;
// sendEvent中事件对象的生命周期由Qt程序管理
// 支持 栈事件对象 和 堆事件对象 的发送
[static] bool sendEvent(QObject *receiver, QEvent *event)
- 非阻塞型事件发送:事件发送后会立即返回,被发送出去的事件会在事件队列中等待处理。
// postEvent中事件对象的生命周期由Qt平台管理
// 只能发送堆事件对象,事件被处理后由Qt平台销毁
[static] void postEvent(QObject *receiver, QEvent *event, int priority = Qt::NormalEventPriority)
可以从下面的例子中体会sendEvent和postEvent的使用,两种方式使用结果显而易见:
// widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QPushButton>
class Widget : public QWidget
{
Q_OBJECT
QPushButton button;
public:
explicit Widget(QWidget *parent = nullptr);
~Widget();
protected:
bool event(QEvent *event);
private:
void testSendEvent();
void testPostEvent();
private slots:
void onButtonClick();
};
#endif // WIDGET_H
// widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QApplication>
#include <QDebug>
#include <QMouseEvent>
Widget::Widget(QWidget *parent) : QWidget(parent)
{
this->resize(300, 300);
button.setParent(this);
button.setText("Click me");
button.resize(100, 30);
button.move(10, 10);
connect(&button, QPushButton::clicked, this, Widget::onButtonClick);
}
Widget::~Widget(){}
void Widget::onButtonClick(){
// testSendEvent();
testPostEvent();
}
void Widget::testSendEvent(){
qDebug() << "Send event begin...";
QMouseEvent evt(QEvent::MouseButtonPress, QPoint(0, 0), Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
QApplication::sendEvent(this, &evt);
qDebug() << "Send event end...";
}
void Widget::testPostEvent() {
qDebug() << "Post event begin...";
// 使用postEvent,事件对象必须是堆对象,否则会程序崩溃
// QMouseEvent evt(QEvent::MouseButtonPress, QPoint(0, 0), Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
QMouseEvent *evt = new QMouseEvent(QEvent::MouseButtonPress, QPoint(0, 0), Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
QApplication::postEvent(this, evt);
qDebug() << "Post event end...";
}
bool Widget::event(QEvent *event) {
if(event->type() == QEvent::MouseButtonPress) {
qDebug() << "event: " << event;
}
return QWidget::event(event);
}
sendEevent()调用结果:
postEvent()调用结果:
自定义事件创建
Qt除了平台的默认事件外,还支持自定义事件类设定和使用。自定义事件必须遵循以下几点准则,缺一不可:
- 自定义事件类必须继承QEvent;
- 自定义事件类必须拥有全局唯一的Type值,用来标记事件的ID,这是Qt平台规定必须要有;
- 程序中必须提供自定义事件对象的处理方法。
关于如何定义事件ID,其实很简单,只要遵循Qt平台的规则即可。Qt提供了QEvent::User和 QEvent::MaxUser枚举,这意思是表示Qt允许开发者们可以把事件ID设定在QEvent::User之后的值(QEvent::User + Value),但不能超过最大值QEvent::MaxUser。
class TestEvent : public QEvent {
public:
static const Type type = static_cast<Type>(QEvent::User + 0x01);
// do something...
}
自定义事件类对象创建后,千万别忘记了处理自定义事件类的方法。方法有两种:
- 将事件过滤器安装到目标对象,在eventFilter()函数中编写自定义事件的处理逻辑;
- 在目标对象的类中重写事件处理函数,在event()函数中编写自定义事件的处理逻辑。
模拟一个点击按钮,将数据更新到QLineEdit中。通过自定义事件类来进行数据的传递,当然这仅仅这是一个模拟举例,自定义事件类的封装很强大,甚至可以作为多线程与UI组件交互的桥梁,后续文章会介绍到。
新增一个StringEvent事件类:
// stirngevent.h
#ifndef STRINGEVENT_H
#define STRINGEVENT_H
#include <QEvent>
#include <QString>
class StringEvent : public QEvent
{
QString m_data;
public:
const static Type type = static_cast<Type>(QEvent::User + 0x01);
StringEvent(QString data);
QString data();
};
#endif // STRINGEVENT_H
// stringevent.cpp
#include "stringevent.h"
StringEvent::StringEvent(QString data):QEvent(type) {
m_data = data;
}
QString StringEvent::data() {
return m_data;
}
StringEvent的使用:
#include "widget.h"
#include <QApplication>
#include <QDebug>
#include "stringevent.h"
Widget::Widget(QWidget *parent) : QWidget(parent)
{
this->resize(300, 300);
QVBoxLayout *vLayout = new QVBoxLayout(this);
m_button.setText("Click me");
connect(&m_button, QPushButton::clicked, this, Widget::onButtonClick);
vLayout->addWidget(&m_lineEdit);
vLayout->addWidget(&m_button);
m_button.installEventFilter(this);
}
Widget::~Widget(){}
void Widget::onButtonClick(){
qDebug() << "Button click begin...";
StringEvent strEvt("Vincent Lin...");
QApplication::sendEvent(&m_button, &strEvt);
qDebug() << "Button click end...";
}
bool Widget::eventFilter(QObject *watched, QEvent *event) {
if( watched == &m_button && event->type() == StringEvent::type ) {
StringEvent *strEvent = dynamic_cast<StringEvent*>(event);
QString data = strEvent->data();
m_lineEdit.setText(data);
return true;
}
return QWidget::eventFilter(watched, event);
}