一、Qt中事件过滤器详解
一.什么是事件过滤器?
事件过滤器是Qt中的一种机制,允许您在一个对象上截获和处理其他对象的事件。
通过使用事件过滤器,您可以在事件到达其目标对象之前拦截它,并对事件进行处理或修改。
2.为什么使用事件过滤器?
使用事件过滤器有以下几个优点:
1.事件过滤器可以提供更灵活的事件处理方式,因为它可以应用于多个对象。
2.事件过滤器可以用于对特定事件进行全局的拦截和处理,而不需要修改原始对象的代码。
3.事件过滤器可以实现对事件的修改、屏蔽或延迟处理等高级操作。
二.实现事件过滤器:
要实现事件过滤器,您需要创建一个继承自QObject的类,并重写它的eventFilter()函数。该函数会在事件到达对象时被调用,您可以在其中处理事件并返回布尔值来指示是否拦截该事件。
我们先看下另外两个相关的方法,一个是给对象安装某个事件过滤器,一个是移除对应的事件过滤器。
void QObject::installEventFilter(QObject *filterObj)
void QObject::removeEventFilter(QObject *obj)
事件过滤器使用的三种方式:
1、父窗口类通过重写eventFilter方法来监听子控件的相关事件进行处理。
使用这种方式的好处是不需要通过重写控件的方式获取某些事件,对于安装了事件过滤器的对象,他们所有的事件都会经过这个事件过滤器,所以就可以直接在父窗口中进行监测。
下面这个例子中MyLineEdit和MyBtn继承了QLineEdit和QPushButton,分别重写了两者的键盘按下(keyPressEvent)和鼠标按下事件(mousePressEvent),然后在他们的父窗口EventTestWgt中重写了事件过滤器(eventFilter),并给MyLineEdit和MyBtn对象及本身都安装了事件过滤器。
在此过滤器中捕捉到相应的事件,通过返回true,过滤输入框的键盘按下事件、过滤按钮的鼠标按下事件,过滤本身的鼠标按下事件,通过返回false,让本身的键盘按下事件继续传递,所以我们看到MyLineEdit的keyPressEvent方法、MyBtn的mousePressEvent以及EventTestWgt的mousePressEvent都不会被调用,只有EventTestWgt的keyPressEvent会被调用。
EventTestWgt.h
class MyLineEdit : public QLineEdit
{
public:
MyLineEdit(QWidget* parent = nullptr);
private:
void keyPressEvent(QKeyEvent *event);
};
class MyBtn : public QPushButton
{
Q_OBJECT
public:
MyBtn(QWidget* parent = nullptr);
private:
void mousePressEvent(QMouseEvent *event);
};
class EventTestWgt : public QWidget
{
Q_OBJECT
public:
EventTestWgt(QWidget *parent = nullptr);
~EventTestWgt();
private:
void initWgt();
void initConnections();
private:
void keyPressEvent(QKeyEvent *event);
void mousePressEvent(QMouseEvent *event);
private:
bool eventFilter(QObject *watched, QEvent *event);
private slots:
void onBtnClicked();
private:
MyLineEdit* m_lineEdit;
MyBtn* m_pBtn;
};
EventTestWgt.cpp
#include "EventTestWgt.h"
#include <QDebug>
#include <QHBoxLayout>
#include <QKeyEvent>
#include <QMouseEvent>
#include <QEvent>
MyLineEdit::MyLineEdit(QWidget* parent /*= nullptr*/)
: QLineEdit(parent)
{
}
void MyLineEdit::keyPressEvent(QKeyEvent *event)
{
qDebug() << "MyLineEdit::keyPressEvent" << event->key();
return QLineEdit::keyPressEvent(event);
}
MyBtn::MyBtn(QWidget* parent /*= nullptr*/)
{
}
void MyBtn::mousePressEvent(QMouseEvent *event)
{
qDebug() << "MyBtn::mousePressEvent";
return QPushButton::mousePressEvent(event);
}
EventTestWgt::EventTestWgt(QWidget *parent)
: QWidget(parent)
{
initWgt();
initConnections();
this->resize(300, 200);
}
EventTestWgt::~EventTestWgt()
{
}
void EventTestWgt::initWgt()
{
// 给自己安装事件过滤器;
this->installEventFilter(this);
// 给输入框和按钮都安装上事件过滤器;
m_lineEdit = new MyLineEdit;
m_lineEdit->installEventFilter(this);
m_pBtn = new MyBtn;
m_pBtn->setText("MyBtn");
m_pBtn->installEventFilter(this);
QHBoxLayout* hLayout = new QHBoxLayout(this);
hLayout->addStretch();
hLayout->addWidget(m_lineEdit);
hLayout->addStretch();
hLayout->addWidget(m_pBtn);
}
void EventTestWgt::initConnections()
{
connect(m_pBtn, &QPushButton::clicked, this, &EventTestWgt::onBtnClicked);
}
void EventTestWgt::keyPressEvent(QKeyEvent *event)
{
qDebug() << "EventTestWgt::keyPressEvent";
return QWidget::keyPressEvent(event);
}
void EventTestWgt::mousePressEvent(QMouseEvent *event)
{
qDebug() << "EventTestWgt::mousePressEvent";
return QWidget::mousePressEvent(event);
}
bool EventTestWgt::eventFilter(QObject *watched, QEvent *event)
{
if (watched == m_lineEdit)
{
// 过滤处理输入框键盘按下事件;
if (QEvent::KeyPress == event->type())
{
// todo;
return true;
}
}
if (watched == m_pBtn)
{
// 过滤处理MyBtn的鼠标按下事件;
if (QEvent::MouseButtonPress == event->type())
{
// todo;
return true;
}
}
if (watched == this)
{
// 过滤处理自己的鼠标按下事件;
if (QEvent::MouseButtonPress == event->type())
{
// todo;
return true;
}
// 对自己的键盘按下事件不处理;
if (QEvent::KeyPress == event->type())
{
// todo;
return false;
}
}
return QWidget::eventFilter(watched, event);
}
void EventTestWgt::onBtnClicked()
{
qDebug() << "EventTestWgt::onBtnClicked";
}
注意,因为Qt在按钮控件的内部也是通过事件的捕捉来发送信号的,这里过滤了按下事件会影响信号的发送,特此注意。
2、专门的事件过滤器类,对特定的对象/特定的事件进行处理
事件过滤器类只需对当前安装的对象进行处理,无需关心其他操作,且一个事件过滤器类可以被多个对象使用,例如Qt文档中的按键过滤示例,KeyPressEnter类中的eventFilter过滤了所有的键盘按下事件,只要安装此事件过滤器的控件,都接收不到键盘按键按下的事件,这种就是对某个通用的事件进行过滤,可以进行多次复用。
class KeyPressEater : public QObject
{
Q_OBJECT
...
protected:
bool eventFilter(QObject *obj, QEvent *event) override;
};
bool KeyPressEater::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
qDebug("Ate key press %d", keyEvent->key());
return true;
} else {
// standard event processing
return QObject::eventFilter(obj, event);
}
}
void test()
{
KeyPressEater *keyPressEater = new KeyPressEater(this);
QPushButton *pushButton = new QPushButton(this);
QListView *listView = new QListView(this);
pushButton->installEventFilter(keyPressEater);
listView->installEventFilter(keyPressEater);
}
3、给QApplication安装事件过滤器,达到全局事件监听的效果
在notify方法下发事件的时候,QApplication对象可以拿到第一控制权,对某些事件优先进行处理,比如全局的快捷键操作。
使用上方的KeyPressEater类对全局的键盘按下事件进行过滤.
QApplication a(argc, argv);
KeyPressEater *keyPressEater = new KeyPressEater(&a);
a.installEventFilter(keyPressEater);