写在前面
在 Qt 中,事件分发器(Event Dispatcher)是一个核心概念,用于处理 GUI 应用程序中的事件。事件分发器负责将事件从一个对象传递到另一个对象,直到事件被处理或被取消。
每个继承自QObject或QObject的类都可以在本类中重写bool event(QEvent *e),来实现相关事件的捕获和拦截。
事件分发器的工作原理
event函数
每个继承自QObject或QObject的类都可以在本类中重写bool event(QEvent *e),来实现相关事件的捕获和拦截,原型如下:
bool event(QEvent *e);
e: 存储事件相关信息
返回值:返回true表示拦截处理,不再向下分发
这里使用一个简单的示例图帮助理解:
我们知道,没有application应用程序运行后,都有一个事件循环,即exec()。
每个事件都会有一个“四元组”标识事件来自哪个对象,哪个事件触发(信号),要发往哪个对象,使用哪个函数(槽)处理。
而事件分发器则工作在信号发往目的对象的这一阶段,即某一控件对象收到来自事件循环的某一需要该对象处理的事件(信号)时,就会进入到事件分发器,在事件分发器中,可以会有一些事件类型的判断,根据类型进行相应的处理(分发)。
例,在事件分发器中判断,若是鼠标按下事件,则使用mouPressEvent进行专门处理,若是鼠标移动事件,则使用mouseMoveEvent进行相应处理。
可以这样理解:
bool QLabel::event(QEvent* e)
{
if (是鼠标按下事件)
{
//调用mousePressEvent处理,mousePressEvent是虚函数,会动态调用对应版本
mousePressEvent();
return true; //返回true表示成功拦截处理
}
else if (鼠标移动事件)
{
//同理,调用mouseMoveEvent虚函数进行相应处理
mouseMoveEvent();
return true;
}
//也可以继续分发给父类处理
return QFrame::event(e);
}
示例
这里在一个自定义的Label控件中拦截鼠标按下事件:
#ifndef MYLABEL_H
#define MYLABEL_H
#include <QLabel>>
class MyLabel : public QLabel
{
Q_OBJECT
public:
explicit MyLabel(QWidget *parent = nullptr);
//重写鼠标相关事件
void enterEvent(QEnterEvent *event);
void leaveEvent(QEvent *event);
void mousePressEvent(QMouseEvent *ev);
void mouseReleaseEvent(QMouseEvent *ev);
void mouseMoveEvent(QMouseEvent *ev);
//事件分发器
bool event(QEvent *e);
signals:
};
#endif // MYLABEL_H
#include "mylabel.h"
#include <QDebug>
#include <QMouseEvent>
#include <QEvent>
MyLabel::MyLabel(QWidget *parent)
: QLabel{parent}
{
//设置鼠标追踪
setMouseTracking(true);
}
//重写鼠标相关事件
void MyLabel::enterEvent(QEnterEvent *event)
{
qDebug() << "鼠标进入";
}
void MyLabel::leaveEvent(QEvent *event)
{
qDebug() << "鼠标离开";
}
void MyLabel::mousePressEvent(QMouseEvent *ev)
{
//if (ev->button() == Qt::LeftButton)
{
QString qs = QString("鼠标按下, x = %1, y = %2").arg(ev->x()).arg(ev->y());
qDebug() << qs;
}
}
void MyLabel::mouseReleaseEvent(QMouseEvent *ev)
{
qDebug() << "鼠标释放";
}
void MyLabel::mouseMoveEvent(QMouseEvent *ev)
{
//移动需要用buttons函数,位与判断
if (ev->buttons() & Qt::LeftButton)
{
QString qs = QString("鼠标移动了, x = %1, y = %2").arg(ev->x()).arg(ev->y());
qDebug() << qs;
}
}
//事件分发器
bool MyLabel::event(QEvent *e)
{
if (e->type() == QEvent::MouseButtonPress)
{
qDebug() << "event 拦截鼠标按下事件";
return true;
}
return QLabel::event(e);
}
运行可以看到成功在event中拦截到鼠标按下事件,而不再继续分发调用mousePressEvent: