文章目录
Qt 事件概述
- 事件(event)是由窗口系统(操作系统)或者Qt自身产生的,用以响应所发生的各类事件。QT程序是事件驱动的, 程序的每个动作都是由内部某个事件所触发。QT事件的发生和处理成为程序运行的主线,存在于程序整个生命周期
- 常见的事件如:
- 键盘或鼠标事件, 用户按下或者松开键盘或鼠标上的按键,就可以产生一个键盘或者鼠标事件
- 绘制事件,某个窗口第一次显示时就会产生一个绘制事件,用来告诉窗口需要重新绘制它本身,从而使得该窗口可见
- Qt事件,Qt自身也会产生很多事件,比如QObject::startTimer()会触发QTimerEvent事件
- 应用场景
- 全局快捷键/热键
- 输入验证和格式化
- 鼠标手势和拖拽
- 自定义控件行为
Qt事件的表示
- QT将系统产生的消息转化为QT事件,QT事件被封装为对象,所有的QT事件均继承抽象类QEvent,用于描述程序内部或外部发生的动作
- 特殊情况: 在复写 closeEvent 时,注意如果调用 event->ignore() ,意味着关闭事件没有被处理,窗口不关闭;不调用(默认)或者显式调用了另外一个函数 event->accept() 则代表接受事件, 窗口允许被关闭
Qt 事件的处理顺序
- 谁来接受和处理事件:答案是QObject
- QObject 类是整个Qt对象模型的心脏,事件处理机制是QObject三大职责(内存管理、内省(intropection)与事件处理制)之一。任何一个想要接受并处理事件的对象均须继承自QObject,可以选择重载QObject::event()函数或事件的处理权转给父窗口
- Qt框架内部为我们提供了一系列的事件处理机制,当窗口事件产生之后,事件会经过:事件派发 -> 事件过滤->事件分发->事件处理几个阶段
- Qt窗口中对于产生的一系列事件都有默认的处理动作,如果我们有特殊需求就需要在合适的阶段重写事件的处理动作
- 每一个Qt应用程序都对应一个唯一的 QApplication应用程序对象,然后调用这个对象的exec()函数,这样Qt框架内部的事件检测就开始了(程序将进入事件循环来监听应用程序的事件)
cpp
复制代码
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();//开启事件循环
}
- 事件在Qt中产生之后的分发过程如下:
- 当事件产生之后,Qt使用用应用程序对象调用notify()函数将事件发送到指定的窗口
cpp
复制代码
[override virtual] bool QApplication::notify(QObject *receiver, QEvent *e)
复制代码
- 注意,事件在发送之前可以通过事件过滤器进行过滤,默认不对任何产生的事件进行过滤
cpp
复制代码
[virtual] bool QObject::eventFilter(QObject *watched, QEvent *event)
复制代码
- 当事件发送到指定窗口之后,窗口的事件分发器会对收到的事件进行分类
cpp
复制代码
[virtual] bool QObject::event(QEvent *e) //QWidget中重写了该方法
复制代码
- 事件分发器会将分类之后的事件(鼠标事件、键盘事件、绘图事件。。。)分发给对应的事件处理函数进行处理,每个事件处理函数都有默认的处理动作(我们也可以重写这些事件处理器函数),比如:鼠标事件
- QWidget类是Qt中所有窗口类的基类,在这个类里边定义了很多事件处理器函数,它们都是受保护的虚函数。 各个子组件中可以覆盖这些虚函数,例如,QPushButton中:
cpp
复制代码
// /opt/Qt/6.2.4/Src/qtbase/src/widgets/widgets/qabstractbutton.cpp
void QAbstractButton::mouseReleaseEvent(QMouseEvent *e)
{
Q_D(QAbstractButton);
if (e->button() != Qt::LeftButton) {
e->ignore();
return;
}
d->pressed = false;
if (!d->down) {
// refresh is required by QMacStyle to resume the default button animation
d->refresh();
e->ignore();
return;
}
if (hitButton(e->pos())) {
d->repeatTimer.stop();
d->click(); //发射 click()信号
e->accept();
} else {
setDown(false);
e->ignore();
}
}
覆盖过滤器eventFilter
- 实现一个事件过滤包括两个步骤:
- 在目标对象上调用installEventFilter(),注册监视对象
- 在监视对象的eventFilter()函数中处理目标对象的事件
cpp
复制代码
void QObject::installEventFilter(QObject *filterObj)
//filterObj, 实现了eventFilter(QObject *obj, QEvent *event)的对象
//在一个对象上安装事件过滤器filterObj
//这样发往monitoredObj对象的所有事件,都会调用filterObj.eventFilter()进行过滤
monitoredObj->installEventFilter(filterObj);
cpp
复制代码
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
//给pushbutton添加过滤器
ui->pushButton->installEventFilter(this);
}
//过滤器中覆盖默认的eventFilter
bool MainWindow::eventFilter(QObject *obj, QEvent *event){
if(event->type() == QEvent::MouseButtonPress){
qDebug()<<QString("%1 is press!").arg(obj->objectName());
QPushButton *btn = static_cast<QPushButton *>(obj);
qDebug()<<QString("%1 is press!").arg(btn->text());
// 返回 true 表示事件已被处理,不再传递
// 返回 false 表示继续传递
return true;
}
return QWidget::eventFilter(obj, event);
}
覆盖事件处理函数
cpp
复制代码
void MainWindow::closeEvent(QCloseEvent *ev){
QMessageBox::Button btn = QMessageBox::question(this, "关闭窗口", "您确定要关闭窗口吗?");
if(btn == QMessageBox::Yes)
{
// 接收并处理这个事件
ev->accept();
}
else
{
// 忽略这个事件
ev->ignore();
}
}
void MainWindow::resizeEvent(QResizeEvent *ev){
qDebug() << "oldSize: " << ev->oldSize()
<< "currentSize: " << ev->size();
}