前面讲解的事件处理函数,比如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;

此时,运行结果:
image-20260820125216154

此时的事件传递流程:
(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();
}

可见程序启动后,就是进入一个事件的循环,事件的传递流程如下:
image-20260820125231850

图示中除了notify()函数,其他都讲到了:

  • notify()QCoreApplication类中的方法,QApplication是其子类。main()函数最后调用QApplication::exec(),就进入了事件循环。
  • 如果要重写notify()函数,需要自定义一个QApplication类。实际工作中,一般不会重写notify()函数,这里就略过!