前面讲解的事件处理函数,比如mouseMoveEvent()函数,以及事件分发函数event(),都是事件已经到达了控件
问:那么在事件到达控件之前,能不能提前拦截,或者说过滤呢?
答:可以,给控件安装事件过滤器。
1. 重写 eventFilter() 函数
首先,来到propagate_widget.cpp构造中,为LabelPropagate安装事件过滤器,如下:
PropagateWidget::PropagateWidget(QWidget* parent) : QWidget{parent}
{
// ...
lbl->installEventFilter(this);
}
然后,修改PropagateWidget类,声明并实现eventFilter()函数,如下:
// propagate_widget.h
class PropagateWidget : public QWidget {
protected:
bool eventFilter(QObject* watched, QEvent* event);
};
// propagate_widget.cpp
bool PropagateWidget::eventFilter(QObject* watched, QEvent* event)
{
if (watched == lbl && event->type() == QEvent::MouseButtonPress) {
qDebug() << "PropagateWidget::eventFilter()";
}
return QWidget::eventFilter(watched, event);
}
2. eventFilter()的返回值
事件过滤函数eventFilter()返回一个bool值
- 返回 true:则事件到此为止,不再向下传递,也就是不再传递到对应的控件
- 返回 false:则事件继续传递,也就是接着会传递到对应的控件
通常在自定义的事件过滤函数的末尾,调用父类的实现。
父类中eventFilter()函数,在QObject类中实现,源码对应qobject.cpp(在qwidget.cpp中没有实现):
bool QObject::eventFilter(QObject * /* watched */, QEvent * /* event */)
{
return false;
}
可见,只是简单地返回了一个false,表示让事件接着传递。
因此:
// 调用父类的实现
return QWidget::eventFilter(watched, event);
等价于
return false;
此时,运行结果:
此时的事件传递流程:
(1)事件首先到达PropagateWidget中的eventFilter()函数
(2)由于上一步的eventFilter()函数,如果调用父类的eventFilter()函数,或者直接返回false,都表示要继续事件的传递。因此事件被传递到LabelPropagate中的event()函数
(3)由于LabelPropagate中的event()函数,直接调用父类的event()函数。因此事件被分发到LabelPropagate中的mouseMoveEvent()函数
(4)由于LabelPropagate中的mouseMoveEvent()函数,忽略了该事件,
因此事件又被传递到PropagateWidget中的mouseMoveEvent()函数
3. 事件传递-画图演示
从 Qt 程序的入口开始:
#include "mywindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
// 1. QApplication 是 Qt 框架提供的应用程序类
// 作用:负责 Qt 中事件的处理,比如鼠标的单击事件,键盘的输入事件等
QApplication a(argc, argv);
// 2. 创建自己的窗口对象,并调用其 show 方法,将窗口显示出来
MyWindow w;
w.show();
// 3. 调用 QApplication 类的 exec 方法,应用程序就阻塞在这里,并不会退出,而是进入到事件循环的处理, 直到退出程序(比如点击了窗体右上角的关闭按钮)
return a.exec();
}
可见程序启动后,就是进入一个事件的循环,事件的传递流程如下:
图示中除了notify()函数,其他都讲到了:
notify()是QCoreApplication类中的方法,QApplication是其子类。main()函数最后调用QApplication::exec(),就进入了事件循环。- 如果要重写
notify()函数,需要自定义一个QApplication类。实际工作中,一般不会重写notify()函数,这里就略过!







