一、Qt事件简介:
1、 事件是对应用程序内部或者外部产生的事情或者动作的通称。
2、 Qt程序是事件驱动的,Qt事件的产生和处理是程序运行的主线,存在于程序的整个生命周期。
3、 Qt事件被封装为对象,所有的Qt事件均继承自抽象类QEvent。
4、 常见的事件如下:
(1)键盘事件: QKeyEvent
(2)鼠标事件: QMouseEvent
(3)拖拽事件: QDragEnterEvent
(4)滚轮事件: QWheelEvent
(5)绘制事件:QPaintEvent
(6)定时器事件: QTimerEvent
(7)焦点事件:QFocusEvent
(8)关闭事件:QCloseEvent
(9)窗口大小位置引起的事件:QResizeEvent
(10)上下文菜单事件:QContextMenuEvent
5、 一个事件可能包含多种事件类型,这些事件类型统一由QEvent::Type枚举类型表示,通过QEvent类的type()可获得具体的事件类型。如鼠标事件QMouseEvent又分为鼠标按下、鼠标释放、鼠标移动、鼠标双击事件类型。
6、 QEvent子类只能表示事件,但不能处理事件,在Qt中,任何QObject子类实例都可以接收和处理事件。(QObject三大职责: 内存管理、事件处理、内省(intropection))
7、 可在Qt帮助文档中搜索The Event System关键字查看。
二、Qt事件的产生和发送:
1、 事件可能由应用程序外部产生,即操作系统事件; 也可能由应用程序内部产生。
2、 由应用程序外部产生:
(1)、一般用户操作,如鼠标按下、键盘按键等,操作系统(的设备驱动程序)感知,向应用程序发送系统消息。
(2)、Qt的事件循环(非GUI程序的QCoreApplication或GUI程序的QApplication的exec()),监听到事件后生成一个事件对象(QEvent子类对象),放入到事件循环队列中。
(3)、Qt(QCoreApplication或QApplication)从事件循环队列中,取出一个事件对象,通过层层传递,最终会发送给目标QObject对象(一般是获得焦点的QWidget)。
(4)、如果目标QObject对象有自定义安装的事件过滤器,可能会在自定义事件过滤器中处理掉事件;如果没有自定义事件过滤器,则事件会到达目标QObject的event(QEvent *)函数。
(5)、event(QEvent *)函数会根据事件类型(QEvent的type())调用不同的事件处理函数。
(6)、在事件处理函数中发送预定义的信号,从而调用与信号关联的槽函数。
3、 由应用程序内部产生:
(1)、应用程序自己产生事件,有两种方式。
一种是调用QCoreApplication的postEvent(QObject *receiver, QEvent *event, int priority = Qt::NormalEventPriority);
另一种是调用QCoreApplication的sendEvent(QObject *receiver, QEvent *event)。
(2)、postEvent和sendEvent区别:
a、sendEvent会使用notify()函数直接给receiver发送事件; postEvent会向事件队列中添加receiver和event。
b、sendEvent使用同步处理事件(阻塞式的),postEvent使用异步处理事件(非阻塞式的)。
c、sendEvent支持栈/堆空间的事件对象,postEvent只支持堆空间的事件对象。(堆空间事件对象被处理后,会由Qt内部自动摧毁)
4、 事件是发生了某个事情或者某个动作产生的,而信号是由某个对象发射的。
比如单击鼠标这个动作产生了QMouseEvent事件; 而因为单击鼠标时,鼠标的光标正好在按钮上,按钮被按下了,所以按钮会发射clicked()信号。
5、 QApplication是继承自QCoreApplication的,QCoreApplication中的函数QApplication一般都会拥有;
QCoreApplication是非GUI程序,而QApplication是GUI程序,一般QApplication用的多,以下都用QApplication举例。
三、Qt事件的传递和处理:
1、 事件传递是从QApplication开始,层层往下最终到达目标QObject对象(一般是获得焦点的QWidget);如果目标QObject对象处理不了,事件又被层层传递给父Widget。
2、 在传递过程中,事件可以被处理,就对应着不同级别的事件处理方法,有5种不同级别的处理方法,从上往下如下:
(1)、继承QApplication,重写notify()。(bool QCoreApplication::notify(QObject *receiver, QEvent *event))
a、Qt调用notify()来发送事件。
b、重写notify()是可以得到所有事件的方法。
c、在其它事件过滤器之前得到事件。
d、但notify()一次只能处理一个事件。
(2)、给QApplication对象安装事件过滤器。
a、一个程序中,QApplication对象只有一个。
b、如果QApplication对象安装了事件过滤器,那么程序中所有对象的事件都要送到eventFilter()函数中。
c、可以同时处理多个事件。
(3)、在目标QObject对象上安装事件过滤器(详情看四)。
a、目标QObject对象调用installEventFilter()函数,目的是在本对象上安装一个事件监控(过滤)对象。(void QObject::installEventFilter(QObject *filterObj))
b、在事件监控(过滤)对象的eventFilter()函数中处理目标对象的事件。(bool QObject::eventFilter(QObject *watched, QEvent *event))
c、事件在到达目标QObject对象时,优先会调用事件监控(过滤)对象的eventFilter()函数。(本质是事件过滤对象的指针被保存在目标QObject对象中,并且eventFilter()函数是公有的可以被调用。)
d、可以在一个界面类中处理不同子部件的不同事件。
(4)、重写目标QObject对象的event(QEvent *)函数。
a、bool QObject::event(QEvent *e)函数会在事件过滤器之后,默认事件处理函数之前调用。
b、event()函数会根据事件类型,将事件分发给不同事件处理函数。
c、event()可以处理那些没有特定事件处理函数的少见的事件。
(5)、重写目标QObject对象的事件处理函数如paintEvent()、mousePressEvent()。
a、最常用但是只能处理特定事件。
3、 2中的(5)、(4)、(3)最常用,但是要注意函数返回值或事件状态的意义,涉及到事件继续往下或往上传递的过程。
四、事件过滤器:
0、 前提:
(1)、名词"组件"、“部件”、“控件”、"QWidget"是一个意思,最顶层父类都是QObject。
(2)、只要是QObject对象,就可以实现监控和被监控。
(3)、注意父类和父组件的区别: 父类表示子类继承的父类; 父组件表示子组件的parent组件。
1、 事件过滤器可以对组件接收到的事件进行监控和过滤。
2、 组件要想被监控,需要调用void QObject::installEventFilter(QObject * filterObj),来安装事件监控(过滤)对象。(本质会将事件监控(过滤)对象的指针存储在组件成员变量中)
3、 事件监控(过滤)对象(一般是父组件)需要重写public函数bool QObject::eventFilter(QObject * watched, QEvent * event),此函数即为事件过滤器。
4、 事件到达组件时,组件都会先调用事件监控(过滤)对象的eventFilter()函数。
5、 eventFilter()函数返回值:
(1) return true; 表明该事件已被处理,不会再将该事件交给组件的其它事件过滤器或event()函数。
(2) return false; 表明该事件需要进一步处理,会将该事件交给组件的其它事件过滤器或event()函数。
(3) return 父类::eventFilter(obj, event); 注意父类是事件监控(过滤)对象的父类。表明不太关注此事件,将此事件交给父类默认的事件过滤器处理,不知返回true还是false。
6、 一个组件可以安装多个事件监控(过滤)对象,会按照安装顺序调用相应的事件过滤器。
7、 举例:
//Widget.h
#pragma once
#include <QWidget>
namespace Ui { class Widget; };
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = Q_NULLPTR);
~Widget();
bool eventFilter(QObject * obj, QEvent * event) override;
private:
Ui::Widget *ui;
};
//Widget.cpp
#include "Widget.h"
#include "ui_Widget.h"
#include <QKeyEvent>
#include <QWheelEvent>
Widget::Widget(QWidget *parent) : QWidget(parent)
{
ui = new Ui::Widget();
ui->setupUi(this);
ui->textEdit->installEventFilter(this); //组件安装事件监控(过滤)对象
ui->spinBox->installEventFilter(this); //组件安装事件监控(过滤)对象
}
Widget::~Widget()
{
delete ui;
}
bool Widget::eventFilter(QObject * obj, QEvent * event) //一个事件过滤器可以监控多个组件的事件
{
if (obj == ui->textEdit)
{
if (event->type() == QEvent::Wheel)
{
QWheelEvent * wheelEvent = dynamic_cast<QWheelEvent *>(event);
if (wheelEvent->delta() > 0)
{
ui->textEdit->zoomIn();
}
else
{
ui->textEdit->zoomOut();
}
return true; //该事件已被处理,不会再继续传递给组件的其它事件过滤器或者event()函数
}
else
{
return false; //其它事件类型则需要进一步处理,会继续传递给组件的其它事件过滤器或event()函数
}
}
else if (obj == ui->spinBox)
{
if (event->type() == QEvent::KeyPress)
{
QKeyEvent * keyEvent = dynamic_cast<QKeyEvent *>(event);
if (keyEvent->key() == Qt::Key_Space)
{
ui->spinBox->setValue(0);
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
else
{
return QWidget::eventFilter(obj, event);
}
}
8、 事件过滤器的好处: 假设一个父控件上有很多不同类型的子控件,就可以不用重写全部子控件的事件处理函数了,只要将子控件都安装事件监控(过滤)对象(父控件),就可以统一在事件过滤器中处理各个子控件的事件了。
五、event():
1、 bool QObject::event(QEvent *e);
2、 事件经过目标QObject对象安装的事件过滤器后,会被传递给目标QObject对象的event()函数。
3、 默认的event()函数并不直接处理事件,主要用于事件的分发。根据事件对象的类型(调用QEvent的type()),分发给不同的事件处理器(event handler)。
4、 如果想要在事件分发之前做一些操作,我们需要重写event()函数。
5、 在event()函数中,事件的传递:
a、返回true,表明事件处理完毕,该事件不会再被传递。
b、返回false,表明事件会传递给父组件处理。
c、调用父类的event(),默认处理,返回结果不确定,可能会先传递给事件处理函数,然后返回。(常用)
6、 注意,在event()函数中,调用事件对象的accept()和ignore()没有作用,不会影响到事件的传播。
7、 可以调用isAccepted()来判断事件的状态。
六、事件处理函数:
1、 所有的事件处理函数,都是protected权限,并且是virtual函数,可供子类调用或重写。
2、 所有的事件处理函数都没有返回值。
3、 一般会在事件处理函数中,发送(emit)预定义的信号,调用与信号关联的槽函数。
4、 在事件处理函数中,事件的传递:
a、事件调用accept()返回,表明组件已经接受并处理该事件,事件不会再被传递(默认)。
b、事件调用ignore()返回,表明组件忽略该事件,该事件会被传递给父组件(parent)。
c、调用父类的默认事件处理函数返回,结果是默认处理,不确定具体状态。
5、 举例:
//MyLineEdit.h
#pragma once
#include <QLineEdit>
class MyLineEdit : public QLineEdit
{
Q_OBJECT
public:
explicit MyLineEdit(QWidget * parent = nullptr);
protected:
void keyPressEvent(QKeyEvent * event);
};
//MyLineEdit.cpp
#include "MyLineEdit.h"
#include <QDebug>
#include <QKeyEvent>
MyLineEdit::MyLineEdit(QWidget * parent) : QLineEdit(parent)
{
}
void MyLineEdit::keyPressEvent(QKeyEvent * event)
{
qDebug() << tr("MyLineEdit键盘按下事件");
//执行父类QLineEidt的默认事件处理。(为了让行编辑器中能够输入字符, 否则行编辑器中无法正常输入字符。)
//所以执行完父类QLineEidt的默认事件处理后, event是什么状态并不太确定, 可能是accept、可能是ignore、可能发送过信号。
/*QLineEdit::keyPressEvent(event);*/
//emit 信号
//表明行编辑器忽略此事件, 此事件会传递给父组件(又是从父组件的事件过滤器开始)。
//所以在行编辑器中每按下一个key,都会出现"MyLineEdit键盘按下事件"和"Widget键盘按下事件"。
/*event->ignore();*/
//表明行编辑器接受并处理此事件,此事件不会再被传递。(默认)
//所以在行编辑器中每按下一个key,只会出现"MyLineEdit键盘按下事件"。
/*event->accept();*/
}
//Widget.h
#include "ui_Widget.h"
class MyLineEdit;
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = Q_NULLPTR);
~Widget();
protected:
void keyPressEvent(QKeyEvent *event);
private:
Ui::Widget ui;
MyLineEdit * lineEdit;
};
//Widget.cpp
#include "Widget.h"
#include <QDebug>
#include "MyLineEdit.h"
Widget::Widget(QWidget *parent) : QWidget(parent)
{
ui.setupUi(this);
lineEdit = new MyLineEdit(this);
lineEdit->move(100, 100);
}
Widget::~Widget()
{
}
void Widget::keyPressEvent(QKeyEvent * event)
{
qDebug() << tr("Widget键盘按下事件");
}