【Qt开发流程】之事件系统1:事件系统描述及事件发生流程
Qt的事件系统
在Qt中,事件是对象,派生自抽象的QEvent类,它表示应用程序内部发生的事情或作为应用程序需要知道的外部活动的结果。事件可以由QObject子类的任何实例接收和处理,但它们与小部件特别相关。以下描述了在典型应用程序中如何传递和处理事件。
需要注意:
事件与信号并不相同,比如单击某个按钮,就会产生鼠标事件(QMouseEvent),这个事件不是按钮产生的,但因为按钮按下了,所以会发射clicked()
单击信号(这个是按钮产生的)。如果只关心信号呢,就不用考虑鼠标事件;但如果要设计一个按钮,或者单击产生别的效果,就要关心鼠标事件了。这两者是两个层面的东西,发出者不同,作用也不同。
此外,在Qt中,任何QObject子类实例都可以接收和处理事件。
QEvent类关系图
如下:
以下链接是拖放事件介绍和使用示例:
【Qt开发流程】之拖放操作1:介绍
链接: https://blog.csdn.net/MrHHHHHH/article/details/134626484
【Qt开发流程】之拖放操作2:使用
链接: https://blog.csdn.net/MrHHHHHH/article/details/134632006
事件类型
大多数事件类型都有特殊的类,特别是QResizeEvent
, QPaintEvent
, QMouseEvent
, QKeyEvent
和QCloseEvent
。每个类都是QEvent
的子类,并添加特定于事件的函数。例如,QResizeEvent
添加了size()和oldSize(),使小部件能够发现它们的尺寸是如何被改变的。
有些类支持不止一种实际事件类型。QMouseEvent
支持鼠标按键、双击、移动和其他相关操作。
每个事件都有一个关联的类型,在QEvent:: type
中定义,这可以用作运行时类型信息的方便来源,以快速确定给定事件对象是从哪个子类构造的。
由于程序需要以各种复杂的方式作出反应,Qt的事件传递机制是灵活的。
当事件发生时,Qt通过构造适当的QEvent子类的实例来创建一个事件对象来表示它,并通过调用其event()函数将其传递给QObject的特定实例(或其子类之一)。
此函数不处理事件本身;根据所交付的事件类型,它为该特定类型的事件调用事件处理程序,并根据该事件是被接受还是被忽略发送响应。
一些事件,如QMouseEvent和QKeyEvent,来自窗口系统;一些,如QTimerEvent,来自其他来源;有些来自应用程序本身。
事件的处理
如何处理一个事件呢?有以下5中处理事件的方法。
根据QCoreApplication::notify(QObject *receiver, QEvent *event)
:
发送事件给接收者:receiver->event(事件)。返回从接收者的事件处理程序返回的值。请注意,对于发送给任何线程中的任何对象的所有事件,都会调用此函数。
对于某些类型的事件(例如鼠标和键事件),如果接收者对事件不感兴趣(即,它返回false),事件将被传播到接收者的父对象,依此类推直到顶级对象。
处理事件有五种不同的方式;重新实现这个虚函数只是其中之一。下面列出了所有五种方法:
- 重新实现了paintEvent(), mousePressEvent()等。这是最常见、最简单、也是最不强大的方法。
- 重新实现这个函数
notify()
。这是非常强大的,提供完全的控制;但是一次只能有一个子类处于活动状态。 - 在QCoreApplication::instance()上安装事件过滤器。这样的事件过滤器能够处理所有小部件的所有事件,因此它与重新实现notify()一样强大;此外,可以有多个应用程序全局事件过滤器。全局事件过滤器甚至可以查看禁用小部件的鼠标事件。请注意,应用程序事件筛选器仅对位于主线程中的对象调用。
- 重新实现QObject::event()(与QWidget一样)。如果这样做,可以按Tab键,并且可以在任何特定于小部件的事件过滤器之前看到事件。
- 在对象上安装事件筛选器。这样的事件过滤器获取所有事件,包括Tab和Shift+Tab键按下事件,只要它们不改变焦点小部件。
在实际编程中,方法1最常用,其次是方法五,方法二和方法三虽然功能强大,但会减缓事件的传递,因此很少用到。
事件过滤
有时,对象需要查看(可能还需要拦截)传递给另一个对象的事件。例如,对话框通常需要过滤某些小部件的按键;例如,修改返回键处理。
QObject::installEventFilter()
函数通过设置一个事件过滤器来实现这一点,使指定的过滤器对象在其QObject::eventFilter()
函数中接收目标对象的事件。事件过滤器在目标对象处理事件之前处理事件,允许它根据需要检查和丢弃事件。现有的事件过滤器可以使用QObject::removeEventFilter()
函数删除。
当调用过滤器对象的eventFilter()实现时,它可以接受或拒绝事件,并允许或拒绝事件的进一步处理。如果所有事件过滤器都允许对事件进行进一步处理(通过每个过滤器返回false),则将事件发送到目标对象本身。如果其中一个停止处理(通过返回true),则目标和任何后续事件过滤器根本无法看到该事件。
bool FilterObject::eventFilter(QObject *object, QEvent *event){if (object == target && event->type() == QEvent::KeyPress) {QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);if (keyEvent->key() == Qt::Key_Tab) {// Special tab handlingreturn true;} elsereturn false;}return false;}
上面的代码显示了拦截发送到特定目标小部件的Tab键按下事件的另一种方法。在这种情况下,过滤器处理相关事件并返回true以阻止它们被进一步处理。所有其他事件都被忽略,过滤器返回false以允许它们通过安装在目标小部件上的任何其他事件过滤器发送到目标小部件。
通过在QApplication或QCoreApplication对象上安装事件过滤器,也可以过滤整个应用程序的所有事件。在特定于对象的筛选器之前调用此类全局事件筛选器。这是非常强大的,但它也减慢了整个应用程序中每个事件的事件交付;通常应该使用讨论的其他技术。
事件的传递
每个程序main()
函数最后都会调用QApplication()
的exec()
方法,它会使Qt应用程序进入事件循环,这样就可以使应用程序在运行时接收发生的各种事件。一旦有事件发生,Qt便会构建一个相应的QEvent子类对象表示,之后它会传递相应的QObject对象或子对象。
传递事件的正常方式是调用虚函数。例如,QPaintEvent是通过调用QWidget::paintEvent()传递的。这个虚拟函数负责做出适当的反应,通常是通过重新绘制小部件。如果在虚函数的实现中没有执行所有必要的工作,则可能需要调用基类的实现。
例如,下面的代码处理自定义复选框小部件上的鼠标左键点击,同时将所有其他按钮点击传递给基类QCheckBox:
void MyCheckBox::mousePressEvent(QMouseEvent *event){if (event->button() == Qt::LeftButton) {// handle left mouse button here} else {// pass on other buttons to base classQCheckBox::mousePressEvent(event);}}
如果想替换基类的函数,必须自己实现所有的东西。但是,如果只想扩展基类的功能,那么可以实现想要的内容,并调用基类来获取不想处理的任何情况的默认行为。
有时,没有这样一个特定于事件的函数,或者特定于事件的函数是不够的。最常见的例子包括按Tab键。通常,QWidget会拦截这些键来移动键盘焦点,但是一些小部件本身需要Tab键。
这些对象可以重新实现QObject::event()
(通用事件处理程序),并在通常的处理之前或之后处理它们的事件,或者它们可以完全替换函数。一个非常不寻常的小部件,既解释Tab,又具有特定于应用程序的自定义事件,可能包含以下event()函数:
bool MyWidget::event(QEvent *event){if (event->type() == QEvent::KeyPress) {QKeyEvent *ke = static_cast<QKeyEvent *>(event);if (ke->key() == Qt::Key_Tab) {// special tab handling herereturn true;}} else if (event->type() == MyCustomEventType) {MyCustomEvent *myEvent = static_cast<MyCustomEvent *>(event);// custom event handling herereturn true;}return QWidget::event(event);}
注意,对于所有未处理的情况,仍然调用QWidget::event(),并且返回值指示是否处理了事件;true值防止将事件发送给其他对象。
事件传递流程图如下:
发送事件
许多应用程序都希望创建和发送它们自己的事件。通过构造合适的事件对象并使用QCoreApplication::sendEvent()
和QCoreApplication::postEvent()
发送事件,可以以与Qt自己的事件循环完全相同的方式发送事件。
sendEvent()
立即处理事件。当它返回时,事件过滤器和/或对象本身已经处理了事件。对于许多事件类,都有一个名为isAccepted()的函数,它告诉您事件是被最后调用的处理程序接受还是拒绝。
postEvent()
将事件发送到队列中以供以后调度。下次Qt的主事件循环运行时,它会调度所有发布的事件,并进行一些优化。例如,如果有多个调整大小事件,它们将被压缩为一个事件。这同样适用于绘制事件:QWidget::update()
调用postEvent()
,这消除了闪烁并通过避免多次重绘提高了速度。
postEvent()
也在对象初始化期间使用,因为提交的事件通常会在对象初始化完成后很快被分派。在实现小部件时,重要的是要认识到,事件可以在其生命周期的早期交付,因此,在其构造函数中,一定要在早期初始化成员变量,以免它有可能接收到事件。
要创建自定义类型的事件,需要定义一个事件号,该事件号必须大于QEvent::User
,并且可能需要创建QEvent
的子类,以便传递关于自定义事件的特定信息。
事件发生流程示例
以下是一个示例,根据打印结果,看下其传递的整个流程:
新建一个自定义QLineEdit子类
customlineedit.h
#ifndef CUSTOMLINEEDIT_H
#define CUSTOMLINEEDIT_H#include <QLineEdit>class CustomLineEdit : public QLineEdit
{Q_OBJECT
public:explicit CustomLineEdit(QWidget *parent = nullptr);signals:public slots:protected:void keyPressEvent(QKeyEvent *e);bool event(QEvent *event);
};#endif // CUSTOMLINEEDIT_H
customlineedit.cpp
#include "customlineedit.h"#include <QKeyEvent>
#include <QDebug>CustomLineEdit::CustomLineEdit(QWidget *parent) : QLineEdit(parent)
{}void CustomLineEdit::keyPressEvent(QKeyEvent *e)
{qDebug().noquote() << "[" << __FILE__ << __LINE__ << "]" << "自定义单行编辑框 键盘按下事件";QLineEdit::keyPressEvent(e);e->ignore();
}bool CustomLineEdit::event(QEvent *event)
{if(event->type() == QEvent::KeyPress){qDebug().noquote() << "[" << __FILE__ << __LINE__ << "]" << "自定义单行编辑框 event()函数";}return QLineEdit::event(event);
}
主窗口:
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>namespace Ui {
class MainWindow;
}class MainWindow : public QMainWindow
{Q_OBJECTpublic:explicit MainWindow(QWidget *parent = nullptr);~MainWindow();protected:void keyPressEvent(QKeyEvent *event);bool eventFilter(QObject *watched, QEvent *event);private:Ui::MainWindow *ui;
};#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"#include <QKeyEvent>
#include <QDebug>MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
{ui->setupUi(this);ui->lineEdit->installEventFilter(this);
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::keyPressEvent(QKeyEvent *event)
{qDebug().noquote() << "[" << __FILE__ << __LINE__ << "]" << "Mainwindow 键盘按下事件";
}bool MainWindow::eventFilter(QObject *watched, QEvent *event)
{if(watched == ui->lineEdit){if(event->type() == QEvent::KeyPress){qDebug().noquote() << "[" << __FILE__ << __LINE__ << "]" << "Mainwindow 事件过滤器";}}return QMainWindow::eventFilter(watched, event);
}
运行,输出如下:
得出结论:
- 先进入父对象事件过滤器,判断对象是不是,是,判断类型,最后返回父类的过滤器事件;
- 进入目标对象的event()事件
- 进入目标对象的具体处理事件
- 进入父对象的事件
流程图如下:
结论
天没降大任于我,照样苦我心智,劳我筋骨
。
相关文章:

【Qt开发流程】之事件系统1:事件系统描述及事件发生流程
Qt的事件系统 在Qt中,事件是对象,派生自抽象的QEvent类,它表示应用程序内部发生的事情或作为应用程序需要知道的外部活动的结果。事件可以由QObject子类的任何实例接收和处理,但它们与小部件特别相关。以下描述了在典型应用程序中…...

初始数据结构(加深对旋转的理解)
力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台备战技术面试?力扣提供海量技术面试资源,帮助你高效提升编程技能,轻松拿下世界 IT 名企 Dream Offer。https://leetcode.cn/problems/rotate-array/submissions/ 与字…...
Android 13 - Media框架(18)- CodecBase
从这一节开始我们会回到上层来看ACodec的实现,在这之前我们会先了解ACodec的基类CodecBase。CodecBase.h 中除了声明有自身接口外,还定义有内部类 CodecCallback、BufferCallback,以及另一个基类 BufferChannelBase,接下来我们会一…...

关于微信公众号授权的几件事
背景 项目需要使用微信公众号发消息,然后就来接入这个微信授权啦,微信公众号发消息前提是还需要用户先关注公众号~ 微信授权是有点恶心的,真的真的需要先配置好环境,开发的话目前是可以使用测试号申请公众号使用测试号的appid~ …...

Docker监控Weave Scope的安装和使用
1.本地安装Weave Scope 1)创建文件夹。 mkdir /usr/local/bin/scope 2)从本地上传文件。 rz scope.bin以资源形式已上传到文章开篇。 3)修改scope.bin文件为可执行文件。 chmod 755 /usr/local/bin/scope/scope.bin 4)执行sco…...

为自己创建的游戏编程源码申请软件著作权详细流程(免费分享模板)
以为我这篇文章制作的游戏申请软件著作权为例 Ren‘py 视觉小说 交互式故事游戏制作过程学习笔记(Windows下实现)(多结局游戏)-CSDN博客 一、网站注册 申请软著时,所有的著作权人都需要在中国版权保护中心官网注册账号,并进行实名认证后,才…...

代币化:2024年的金融浪潮预示着什么?
自“TradFi”领袖到加密专家,各方预测代币化机会高达数十万亿。虽然已有引人注目的用例,但与未来几年可能在链上转移的大量数字化资产相比,这些仅是冰山一角。 代币化何时会变为洪流?什么阻碍了其发展? 今年10月&…...

[学习记录]Node event loop 总结流程图
文章目录 文章来源根据内容输出的流程图待处理遗留的问题参考 文章来源 详解JavaScript中的Event Loop(事件循环)机制 根据内容输出的流程图 待处理 这里从polling阶段开始 好像有些问题 遗留的问题 为什么“在I/O事件的回调中,setImmediate…...

【LeetCode热题100】【双指针】移动零
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。 请注意 ,必须在不复制数组的情况下原地对数组进行操作。 示例 1: 输入: nums [0,1,0,3,12] 输出: [1,3,12,0,0] 示例 2: 输入: nums [0] 输出…...

Mybatis 分页查询的三种实现
Mybatis 分页查询 1. 直接在 sql 中使用 limit2. 使用 RowBounds3. 使用 Mybatis 提供的拦截器机制3.1 创建一个自定义拦截器类实现 Interceptor3.2 创建分页查询函数 与 sql3.3 编写拦截逻辑3.4 注册 PageInterceptor 到 Mybatis 拦截器链中3.5 测试 准备一个分页查询类 Data…...

各类声音数据集大合集—乐器、车辆、鸟鸣、蜜蜂声音、歌曲、喇叭、人类声音不同等类型的声音数据集
最近收集了一大波关于各类声音的数据集,包含乐器、车辆、鸟鸣、蜜蜂声音、歌曲、喇叭、人类声音不同等类型的声音数据集,废话不多说,给大家逐一介绍!! 1、吉他和弦大调、小调数据集 吉他和弦大调、小调数据集&#x…...

java设计模式学习之【原型模式】
文章目录 引言原型模式简介定义与用途实现方式UML 使用场景优势与劣势原型模式在spring中的应用员工记录示例代码地址 引言 原型模式是一种创建型设计模式,它允许对象能够复制自身,以此来创建一个新的对象。这种模式在需要重复地创建相似对象时非常有用…...

链表数组插入排序
InsertSort 插入排序算法,比如打扑克牌的算法时,按照从左到右,找到对应的位置插入排序 最重要的是位置移动 找到对应位置值 #include "iostream" #include "bits/stdc.h"using namespace std;void sort(vector<in…...

MyBatis的创建,简单易懂的一篇blog
文章目录 一、MyBatis是什么二、操作流程三.配置resource总结 一、MyBatis是什么 MyBatis 是⼀款优秀的持久层框架,它⽀持⾃定义 SQL、存储过程以及⾼级映射。MyBatis 去除了⼏乎所有的 JDBC 代码以及设置参数和获取结果集的⼯作。MyBatis 可以通过简单的 XML 或注…...

MOS管的静电击穿问题
MOS管输入电阻很高,为什么一遇到静电就不行了? 静电击穿:由于静电的积累导致电压超过了原本MOS的绝缘能力,导致电流突然增大的现象。 MOS管基础知识了解: G极(gate)—栅极,不用说比较好认 S极(source)—源…...

在线 SQL 模拟器SQL Fiddle使用简介
在线 SQL 模拟器SQL Fiddle使用简介 本文可作为“SQL语言与SQL在线实验工具的使用” https://blog.csdn.net/cnds123/article/details/115038700 一文的补充。 有时候,我们想去验证 SQL语句,却缺少数据库环境,那该怎么办呢? 这…...

仿京东淘宝商品列表筛选组件:实现一个高效的侧边栏弹框筛选功能
仿京东淘宝商品列表筛选组件:实现一个高效的侧边栏弹框筛选功能 一、引言 随着电子商务的快速发展,用户体验成为了竞争的关键因素。在众多的电商网站中,如京东和淘宝,商品列表筛选功能为用户提供了便捷的途径来找到心仪的商品。本…...

软件工程 - 第8章 面向对象建模 - 4 - 物理体系结构建模
构件图 构件图概述 构件图描述了软件的各种构件和它们之间的依赖关系。 构件图的作用 在构件图中,系统中的每个物理构件都使用构件符号来表示,通常,构件图看起来像是构件图标的集合,这些图标代表系统中的物理部件,…...

【智能家居】二、添加火灾检测模块(烟雾报警功能点)
可燃气体传感器 MQ-2 和 蜂鸣器 代码段 controlDevice.h(设备控制)smokeAlarm.c(烟雾报警器)buzzer.c(蜂鸣器)mainPro.c(主函数)运行结果 可燃气体传感器 MQ-2 和 蜂鸣器 代码段 …...
history和hash两种路由模式原理,和优缺点
Hash Hash 模式是在 URL 中使用井号(#)来作为路由的模式。在 Hash 模式下,即使页面刷新,浏览器仍然只会请求页面的初始 HTML 文件,所有的路由变化都会在 URL 前面添加 “#/” 符号。 原理 在 Hash 模式下,路…...

网络编程(Modbus进阶)
思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...

TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...

如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...
【论文笔记】若干矿井粉尘检测算法概述
总的来说,传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度,通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...
Axios请求超时重发机制
Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式: 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...
【JavaSE】绘图与事件入门学习笔记
-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角,以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。 坐标体系-像素 …...

深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...
管理学院权限管理系统开发总结
文章目录 🎓 管理学院权限管理系统开发总结 - 现代化Web应用实践之路📝 项目概述🏗️ 技术架构设计后端技术栈前端技术栈 💡 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 🗄️ 数据库设…...
libfmt: 现代C++的格式化工具库介绍与酷炫功能
libfmt: 现代C的格式化工具库介绍与酷炫功能 libfmt 是一个开源的C格式化库,提供了高效、安全的文本格式化功能,是C20中引入的std::format的基础实现。它比传统的printf和iostream更安全、更灵活、性能更好。 基本介绍 主要特点 类型安全:…...
HTML前端开发:JavaScript 获取元素方法详解
作为前端开发者,高效获取 DOM 元素是必备技能。以下是 JS 中核心的获取元素方法,分为两大系列: 一、getElementBy... 系列 传统方法,直接通过 DOM 接口访问,返回动态集合(元素变化会实时更新)。…...