一、事件分发器
事件分发器是 QObject 基类提供的 虚函数 event(QEvent *e),所有 QObject 子类(如 QWidget、QDialog 等)都继承了该函数。其核心职责是:接收事件、判断事件类型、分发到对应事件处理器,或决定事件是否继续传递。
处理流程 : 事件源(鼠标/键盘/系统) → 事件过滤器(拦截/放行) → 事件分发器(event() 路由) → 事件处理器(如 mousePressEvent)

解读事件分发的完整路径:
-
事件产生:系统或应用程序生成事件(如鼠标点击),封装为
QEvent对象。 -
事件传递:
- 默认情况下:中间对象的
event()函数或特定事件处理器(如mousePressEvent())不会自动触发,除非事件被显式传递(例如调用QWidget::event()或父类事件处理器)。 - 事件过滤器:如果中间对象安装了事件过滤器(
installEventFilter()),过滤器的eventFilter()会优先被调用,可通过返回值决定是否拦截事件。 - 在
event()或eventFilter()中,可以通过返回true拦截事件(停止传递),或返回false继续传递。同一个类中过滤器优先级比重写的event()高。
- 默认情况下:中间对象的
- 目标对象处理:
- 事件到达目标对象后,进入其
event()函数,再分发到对应的事件处理器(如mousePressEvent())。 - 如果事件处理函数中未调用父类的实现(如
QWidget::mousePressEvent()),事件可能被完全消费;否则可能继续向上传递。
- 事件到达目标对象后,进入其
关键点说明:
- 事件过滤器的优先级
- 事件过滤器(
eventFilter())在event()之前执行,若返回true,事件会被拦截,不再进入目标对象的event()或处理函数。
- 事件过滤器(
event()函数的作用:- 重写
event()可以拦截所有事件,通过判断event->type()决定是否处理或传递。 - 若返回
true,事件停止分发;若返回false,事件继续传递到处理函数或父类。
- 重写
- 事件处理函数的默认行为:
- 例如
mousePressEvent()中若不调用父类实现,事件不会传递;若调用父类,可能触发父类的默认行为(如按钮按下动画)。
- 例如
关键场景分析
场景1:中间对象未处理事件
- 若中间对象重写
event()并调用父类实现,或通过事件过滤器返回false,事件可能继续传递。 - 示例:
void ParentWidget::mousePressEvent(QMouseEvent *event) {
// 默认不会自动触发,除非子对象调用父类实现
qDebug() << "Parent will NOT receive child's event by default";
}
场景2:中间对象显式处理事件
- 若中间对象重写
event()并调用父类实现,或通过事件过滤器返回false,事件可能继续传递。 - 示例:
bool ParentWidget::event(QEvent *event) {
if (event->type() == QEvent::MouseButtonPress) {
qDebug() << "Parent intercepted event";
// 返回false允许事件继续传递
return false;
}
return QWidget::event(event);
}
事件过滤器拦截
- 中间对象可通过事件过滤器拦截事件,阻止其到达目标对象。
- 示例:
// 安装事件过滤器
childWidget->installEventFilter(parentWidget);
// 在ParentWidget中重写eventFilter
bool ParentWidget::eventFilter(QObject *obj, QEvent *event) {
if (event->type() == QEvent::MouseButtonPress) {
qDebug() << "Event filtered by parent";
return true; // 拦截事件
}
return false; // 继续传递
}
二、事件过滤器
事件过滤器是一种「观察者模式」的实现:一个对象(过滤器对象)可以监控另一个对象(目标对象)的所有事件,在事件到达目标对象前介入处理。
核心依赖两个关键接口:
QObject::installEventFilter(QObject *filterObj):给目标对象安装过滤器(过滤器对象需重写 eventFilter());
QObject::eventFilter(QObject *watched, QEvent *event):过滤器的核心处理函数,接收目标对象和事件,返回 bool 决定事件是否放行。

demo : 设置事件过滤器,阻拦事件传递到上述的组件
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
bool eventFilter(QObject *,QEvent *);//重写事件过滤处理
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
//主窗口
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
/*
* 事件过滤器可以在程序分发到event事件之前再做一次高级拦截
* 1.给控件安装事件过滤器
* 2.重写eventfilter事件
*/
//安装过滤器
ui->widget_2->installEventFilter(this);
}
//重写eventfilter事件
bool Widget::eventFilter(QObject *obj,QEvent *ev)
{
//对传递到自定义组件的事件进行拦截
if(obj == ui->widget_2){
if(ev->type() == QEvent::MouseButtonPress){
qDebug()<< "事件过滤拦截";
return true; //表示拦截
}
}
//其他组件的事件交由父级处理
return QWidget::eventFilter(obj,ev);
}
Widget::~Widget()
{
delete ui;
}
过滤器卸载
// 窗口关闭时卸载
void MainWindow::closeEvent(QCloseEvent *e) {
targetBtn->removeEventFilter(filter); // 卸载过滤器
QWidget::closeEvent(e);
}
全局过滤器
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
MyGlobalFilter *globalFilter = new MyGlobalFilter(&a);
a.installEventFilter(globalFilter); // 安装全局过滤器
MainWindow w;
w.show();
return a.exec();
}
类型转换:
eventFilter() 接收的是 QEvent*(基类),若需获取事件详情(如鼠标坐标、键盘按键),需用 dynamic_cast 转换为对应事件子类(如 QMouseEvent*、QKeyEvent*),避免类型错误
bool MyFilter::eventFilter(QObject *watched, QEvent *event) {
if (event->type() == QEvent::MouseMove) {
// 安全转换为鼠标事件(多态类,dynamic_cast 确保转换有效)
QMouseEvent *mouseEv = dynamic_cast<QMouseEvent*>(event);
if (mouseEv) { // 转换成功才处理
qDebug() << "鼠标移动坐标:" << mouseEv->pos();
}
}
return false;
}
三、区别与联系
| 特性 | 事件过滤器eventFilter() | 事件分发器event() |
|---|---|---|
| 触发时机 | 事件到达目标对象前 | 事件到达目标对象后 |
| 作用范围 | 可监控多个对象的事件 | 仅处理单个对象内部事件 |
| 安装方式 | 需调用 installEventFilter () | 自动存在,可重写 |
| 典型场景 | 跨组件监控、全局事件处理 | 类内部事件处理、定制化响应 |
四、 常见问题
1. 过滤器不触发?
原因 1:未给目标对象调用 installEventFilter(filterObj)(忘记安装);
原因 2:过滤器对象未重写 eventFilter()(函数签名错误,如漏写 override);
原因 3:目标对象被销毁后,过滤器仍在监控(野指针);
原因 4:事件被其他优先级更高的过滤器拦截(返回 true);
解决:检查安装步骤、函数签名,用 qDebug() 打印 watched 和 event->type(),确认过滤器是否收到事件。
2. 拦截事件后,目标对象功能失效?
原因:返回 true 拦截了目标对象的核心事件(如按钮的鼠标按下事件),导致目标对象无法响应;
解决:仅拦截需要的事件,非关键事件返回 false 放行;若需监控但不拦截,可在处理后返回 false。
3. 内存泄漏?
原因 1:过滤器对象是堆分配(new 创建),未手动释放(需确保父对象或目标对象管理其生命周期);
原因 2:目标对象销毁后,未调用 removeEventFilter(),过滤器链中仍保留无效指针;
解决:
过滤器对象优先用栈分配(如 GlobalFilter filter;),或设父对象(new MyFilter(this));
目标对象销毁前,调用 removeEventFilter() 卸载过滤器。
4. 全局过滤器性能差?
原因:全局过滤器会处理应用内所有事件(频繁触发如鼠标移动),若逻辑复杂会占用大量资源;
bool GlobalFilter::eventFilter(QObject *watched, QEvent *event) {
// 优先判断事件类型,非键盘事件直接放行
if (event->type() != QEvent::KeyPress) return false;
// 后续处理键盘事件...
}

1万+

被折叠的 条评论
为什么被折叠?



