Qt 框架内部为我们提供了一系列的事件处理机制,当窗口事件产生之后,事件会经过:事件派发 ->
事件过滤->事件分发->事件处理几个阶段。
每一个 Qt 应用程序都对应一个唯一的 QApplication 应用程序对象,然后调用这个对象的 exec() 函数,这样 Qt
框架内部的事件检测就开始了(程序将进入事件循环来监听应用程序的事件)。
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
事件分发过程:
当事件产生之后,Qt 使用用应用程序对象调用 notify() 函数将事件发送到指定的窗口:
[override virtual] bool QApplication::notify(QObject *receiver, QEvent *e);
事件在发送过程中可以通过事件过滤器进行过滤,默认不对任何产生的事件进行过滤:
// 需要先给窗口安装过滤器, 该事件才会触发
void QObject::installEventFilter(QObject *filterObj) //事件过滤器
[virtual] bool QObject::eventFilter(QObject *watched, QEvent *event)
帮助文档:
当事件发送到指定窗口之后,窗口的事件分发器会对收到的事件进行分类:
[override virtual protected] bool QWidget::event(QEvent *event);
帮助文档:
事件分发器会将分类之后的事件(鼠标事件、键盘事件、绘图事件。。。)分发给对应的事件处理器函数进行处理,每个事件处理器函数都有默认的处理动作(我们也可以重写这些事件处理器函数),比如:鼠标事件:
// 鼠标按下
[virtual protected] void QWidget::mousePressEvent(QMouseEvent *event);
// 鼠标释放
[virtual protected] void QWidget::mouseReleaseEvent(QMouseEvent *event);
// 鼠标移动
[virtual protected] void QWidget::mouseMoveEvent(QMouseEvent *event);
事件过滤器例子如下:
如果在部件上安装了事件过滤器,那么,过滤器便可以先于部件捕获事件,从而进行相应的处理。
第一步:定义我们的事件过滤器事件
protected:
bool eventFilter(QObject *obj, QEvent *event) override;
第二步:将需要绑定的对象绑定事件过滤器
ui.label->installEventFilter(this);
ui.label_move->installEventFilter(this);
第三步:重写我们的事件过滤器事件
bool QtEventFilter::eventFilter(QObject* watched, QEvent* event)
{
//需要过滤的对象
if (watched == ui.label)
{
//需要过滤的事件
if (event->type() == QEvent::MouseMove)
{
QMouseEvent* mouseEvent = (QMouseEvent*)event;
QPoint p = mouseEvent->pos();
QString s = QString("x=%1,y=%2").arg(QString::number(p.x())).arg(QString::number(p.y()));
ui.lineEdit_move->setText(s);
}
if (event->type() == QEvent::MouseButtonPress)
{
QMouseEvent* mousePressEvent = (QMouseEvent*)event;
if (mousePressEvent->buttons() & Qt::LeftButton)
{
QPoint p1 = mousePressEvent->pos();
QString s1 = QString("x=%1,y=%2").arg(QString::number(p1.x())).arg(QString::number(p1.y()));
ui.lineEdit_left->setText(s1);
}
}
if (event->type() == QEvent::MouseButtonDblClick)
{
qDebug() << QStringLiteral("label使用MouseButtonDblClick");
}
}
if (ui.label_move == watched)
{
if (event->type() == QEvent::MouseMove)
{
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
qDebug() << QStringLiteral("这是过滤器 MouseMove");
//拦截 不继续往下传递
return true;
}
if (event->type() == QEvent::MouseButtonPress)
{
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
qDebug() << QStringLiteral("这是过滤器 MouseButtonPress ");
//不拦截,继续传递
return false;
}
}
//传递其他事件给基类
return QWidget::eventFilter(watched, event);
}
MyLabel.cpp 自定义Label的鼠标按下和鼠标移动事件
#include "MyLabel.h"
MyLabel::MyLabel(QWidget *parent)
: QLabel(parent)
{
this->setMouseTracking(true); //设置鼠标追踪
}
MyLabel::~MyLabel()
{
}
void MyLabel::mousePressEvent(QMouseEvent *e)
{
qDebug() << QStringLiteral("这是子组件:mousePressEvent");
if (e->buttons() == Qt::LeftButton)
{
qDebug() << QStringLiteral("左键鼠标");
}
else if(e->buttons() == Qt::RightButton)
{
qDebug() << QStringLiteral("右键鼠标");
}
e->ignore();
}
void MyLabel::mouseMoveEvent(QMouseEvent *e)
{
qDebug() << QStringLiteral("这是子组件:mouseMoveEvent x=%1,y=%2").arg(e->x()).arg(e->y());
e->ignore();
}
MyLabel.h
#pragma once
#include <QLabel>
#include <QMouseEvent>
#include <QDebug>
class MyLabel : public QLabel
{
Q_OBJECT
public:
MyLabel(QWidget *parent);
~MyLabel();
protected:
void mousePressEvent(QMouseEvent *);
void mouseMoveEvent(QMouseEvent *);
};
主窗口代码:
QtEventFilter.cpp
#include "QtEventFilter.h"
#include <QDebug>
QtEventFilter::QtEventFilter(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
//安装事件过滤器
ui.label->installEventFilter(this);
ui.label_move->installEventFilter(this);
ui.label->setMouseTracking(true);
}
QtEventFilter::~QtEventFilter()
{
}
bool QtEventFilter::eventFilter(QObject* watched, QEvent* event)
{
if (watched == ui.label)
{
if (event->type() == QEvent::MouseMove)
{
//m_SerPort = Port_Screen;
QMouseEvent* mouseEvent = (QMouseEvent*)event;
QPoint p = mouseEvent->pos();
QString s = QString("x=%1,y=%2").arg(QString::number(p.x())).arg(QString::number(p.y()));
ui.lineEdit_move->setText(s);
}
if (event->type() == QEvent::MouseButtonPress)
{
//m_SerPort = Port_Left;
QMouseEvent* mousePressEvent = (QMouseEvent*)event;
if (mousePressEvent->buttons() & Qt::LeftButton)
{
QPoint p1 = mousePressEvent->pos();
QString s1 = QString("x=%1,y=%2").arg(QString::number(p1.x())).arg(QString::number(p1.y()));
ui.lineEdit_left->setText(s1);
}
}
if (event->type() == QEvent::MouseButtonDblClick)
{
qDebug() << QStringLiteral("label使用MouseButtonDblClick");
}
}
if (ui.label_move == watched)
{
if (event->type() == QEvent::MouseMove)
{
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
qDebug() << QStringLiteral("这是过滤器 MouseMove");
//拦截
return true;
}
if (event->type() == QEvent::MouseButtonPress)
{
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
qDebug() << QStringLiteral("这是过滤器 MouseButtonPress ");
//不拦截,继续传递
return false;
}
}
//传递其他事件
return QWidget::eventFilter(watched, event);
}
void QtEventFilter::mouseDoubleClickEvent(QMouseEvent* event)
{
if (event->type() == QEvent::MouseButtonDblClick)
{
qDebug() << "QEvent::MouseButtonDblClick";
}
}
void QtEventFilter::mousePressEvent(QMouseEvent* event)
{
if (event->type() == QEvent::MouseButtonPress)
{
qDebug() << QStringLiteral("这是父组件 QEvent::MouseButtonPress");
}
}
void QtEventFilter::mouseMoveEvent(QMouseEvent* event)
{
if (event->type() == QEvent::MouseMove)
{
qDebug() << QStringLiteral("这是父组件 QEvent::MouseMove");
}
}
QtEventFilter.h
#include <QtWidgets/QWidget>
#include "ui_QtEventFilter.h"
#include <QRect>
#include <QEvent>
#include <QMouseEvent>
#include <QKeyEvent>
class QtEventFilter : public QWidget
{
Q_OBJECT
public:
QtEventFilter(QWidget *parent = Q_NULLPTR);
~QtEventFilter();
protected:
bool eventFilter(QObject* watched, QEvent* event);
void mouseDoubleClickEvent(QMouseEvent* event);
void mousePressEvent(QMouseEvent* event);
void mouseMoveEvent(QMouseEvent* event);
private:
Ui::QtEventFilterClass ui;
};
我们看一下事件过滤器
true:该ui.lable_move对象的mouseMove事件被拦截,不向下传递执行MyLabel 重写的mousemove事件
false:该ui.lable_move对象的mousepress事件不被拦截,向下继续传递执行MyLabel 重写的mousepress事件
如图:
证明了事件过滤器过滤了MyLabel 重写的mousemove事件。如果没有过滤,会打印“这是子组件:mouseMoveEvent x=%1,y=%2”。
当子组件接受到mousepress事件时:
e->ignore();//忽略这个事件,继续往下传递(传递给它的父类)
e->accept();//接受这个事件,不继续往下传递(传递的下一位对象是它的父类,不是基类)
证明了:子组件在接受事件后,如果四e->ignore 将继续传递给父组件的对应事件
对于被过滤的事件以外的其他事件是没有任何影响的,因为在重写的事件分发器函数的最后调用了父类的事件分发器函数
//父类的事件分发器函数,其他事件按照默认的分发流程进行分发
return QWidget::eventFilter(watched, event);
这样就能保证其他事件按照默认的分发流程进行分发,并最终被窗口处理掉。
ui图:
参考博客:
https://subingwen.cn/qt/event_handler/
https://blog.csdn.net/fengjliu/article/details/48572089