前言
使用事件过滤器之前需要了解qt事件的运行机制。
这里举一个应用场景来记录事件过滤器的整个使用过程。
实现这样一个功能,在一个窗口上追踪鼠标的的移动位置,如果这个窗口是一个单一的窗口,没有任何子控件,那么只需要开启鼠标追踪,然后重写mouseMoveEvent(),就可以实现功能了。
但是如果有子控件的情况下,鼠标移动到子控件上时,移动事件会优先被子控件处理,那如果要在一个复杂的页面上完成这样的功能,岂不是需要重写所有子控件的鼠标事件?
事件过滤机制
Qt 在提供事件循环服务时,为了QObject提供了
bool eventFilter(QObject* watched, QEvent* event) override;
这个函数会在event()函数被调用之前调用,如果返回值为true,那么表面这个事件被处理了,这个事件将跳过event()函数的处理。
并且QObject还提供了
void QObject::installEventFilter(QObject *filterObj)
这个函数可以将一个QObject作为一个过滤器,安装到另一个QObject对象,那么安装之后,在调用自己本身的eventFilter()之前会先调用过滤器的这个函数。
事件过滤器可以设置多个,在调用时会根据安装的先后顺序调用。
示例
#pragma once #include <QWidget> #include <QVBoxLayout> #include <QLabel> #include <QTextEdit> namespace EFT { class widget :public QWidget { public: widget(QWidget* parent = 0); ~widget(); protected: bool eventFilter(QObject* watched, QEvent* event) override; private: QVBoxLayout* vLaout; QLabel* label; QTextEdit* textEdit; }; }
#include "EventFilterTest.h" #include <QEvent> #include <QMouseEvent> #include <QDebug> EFT::widget::widget(QWidget* parent /*= 0*/) :QWidget(parent) { //初始化窗口布局 vLaout = new QVBoxLayout(); label = new QLabel(); label->setText("This is a label"); textEdit = new QTextEdit(); vLaout->addWidget(label); vLaout->addWidget(textEdit); this->setLayout(vLaout); //开启鼠标追踪 this->setMouseTracking(true); label->setMouseTracking(true); textEdit->viewport()->setMouseTracking(true); //安装过滤器 label->installEventFilter(this); textEdit->viewport()->installEventFilter(this); this->installEventFilter(this); } EFT::widget::~widget() { textEdit->deleteLater(); label->deleteLater(); vLaout->deleteLater(); } bool EFT::widget::eventFilter(QObject* watched, QEvent* event) { if (event->type() == QEvent::MouseMove) { auto e = dynamic_cast<QMouseEvent*>(event); label->setText(QString("pos x: %1 y: %2").arg(e->globalX()).arg(e->globalY())); return true; } return QWidget::eventFilter(watched, event); }
运行结果:
-
就是直接给QTextEdit设置过滤器,无法捕捉到事件,猜测时因为这个控件内部被小部件铺满,所以事件被小部件给处理了,所以需要调用viewport()函数获取小部件对象,将过滤器安装到小部件中。
-
如果不给自己安装过滤器,似乎也不会主动的调用eventFilter()函数,这和我之前设想的不太一样。
文章评论