QT EventFilter 事件过滤器实现鼠标追踪

2022年4月7日 3525点热度 2人点赞 0条评论

前言

使用事件过滤器之前需要了解qt事件的运行机制。

这里举一个应用场景来记录事件过滤器的整个使用过程。

实现这样一个功能,在一个窗口上追踪鼠标的的移动位置,如果这个窗口是一个单一的窗口,没有任何子控件,那么只需要开启鼠标追踪,然后重写mouseMoveEvent(),就可以实现功能了。

但是如果有子控件的情况下,鼠标移动到子控件上时,移动事件会优先被子控件处理,那如果要在一个复杂的页面上完成这样的功能,岂不是需要重写所有子控件的鼠标事件?

事件过滤机制

Qt 在提供事件循环服务时,为了QObject提供了

bool eventFilter(QObject* watched, QEvent* event) override;

这个函数会在event()函数被调用之前调用,如果返回值为true,那么表面这个事件被处理了,这个事件将跳过event()函数的处理。

并且QObject还提供了

void QObject::installEventFilter(QObject *filterObj)

这个函数可以将一个QObject作为一个过滤器,安装到另一个QObject对象,那么安装之后,在调用自己本身的eventFilter()之前会先调用过滤器的这个函数。

事件过滤器可以设置多个,在调用时会根据安装的先后顺序调用。

示例

明白了这个机制之后,怎么实现前面的需求也就明了了,只需要创建一个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);
}

运行结果:

注意

  1. 就是直接给QTextEdit设置过滤器,无法捕捉到事件,猜测时因为这个控件内部被小部件铺满,所以事件被小部件给处理了,所以需要调用viewport()函数获取小部件对象,将过滤器安装到小部件中。

  2. 如果不给自己安装过滤器,似乎也不会主动的调用eventFilter()函数,这和我之前设想的不太一样。

大脸猫

这个人虽然很勤快,但什么也没有留下!

文章评论