【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 模式下,路…...

Nginx 具体应用
1 Nginx 1.1 介绍 一款轻量级的 Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器。它占有的内存少,并发能力强,中国大陆使用 nginx 的网站有:百度、京东、新浪、网易、腾讯、淘宝等。第一个公开版本发布于…...

计算机网络之网络传输,三次握手和四次挥手
网络传输通过高低电压 流 基本类型数组 低电压转高电压,通过网卡 传输模式: 全双工:互相传输且能同时传输 半双工:互相传输但是不能同时传输 单工:单向传输,(键盘,显示器&#…...

Azure Machine Learning - 在 Azure AI 搜索中创建全文查询
Azure AI搜索中如果要为全文搜索生成查询,本文提供了设置请求的步骤。 本文还介绍了查询结构,并说明了字段属性和语言分析器如何影响查询结果。 关注TechLead,分享AI全维度知识。作者拥有10年互联网服务架构、AI产品研发经验、团队管理经验&a…...

基于YOLOv8深度学习的钢材表面缺陷检测系统【python源码+Pyqt5界面+数据集+训练代码】目标检测、深度学习实战
《博主简介》 小伙伴们好,我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源,可关注公-仲-hao:【阿旭算法与机器学习】,共同学习交流~ 👍感谢小伙伴们点赞、关注! 《------往期经典推…...

深度学习常见回归分支算法逐步分析,各种回归之间的优缺点,适用场景,举例演示
文章目录 1、线性回归(Linear Regression)1.1 优点1.2 缺点1.3 适用场景1.4 图例说明 2、多项式回归(Polynomial Regression)2.1 优点2.2 缺点2.3 适用场景2.4 图例说明 3、决策树回归(Decision Tree Regression&#…...

Programming Contest 2023(AtCoder Beginner Contest 331)D题 Tile Pattern --- 题解
目录 D - Tile Pattern 题目大意: 思路: 代码: D - Tile Pattern D - Tile Pattern (atcoder.jp) 题目大意: 给你一个n和q,n为局部棋盘大小(n*n) 并且给出局部棋盘中黑白子位置的放置情况,q为查询次数…...

Google测试框架googletest简介与使用方法
环境准备(Ubuntu) 下载 git clone https://github.com/google/googletest.git 安装 cd googletest // 创建build目录 mkdir build cd build //编译安装 cmake .. make sudo make install 检查是否安装成功 ls /usr/local/lib// 存在以下文件则说明…...

进程的创建:fork()
引入 创建进程的方式我们已经学习了一个!在我们运行指令(或者运行我们自己写的可执行程序)的时候不就是创建了一个进程嘛?那个创建进程的方式称为指令级别的创建子进程! 那如果我们想要在代码中创建进程该怎么办呢? fork() for…...

Fabric:创建应用通道
搭建自定义网络可以参考文章: https://blog.csdn.net/yeshang_lady/article/details/134113296 1 创建通道 网络搭建完成之后,就可以开始创建通道了。Fabric V2.5.4中可以在不创建系统通道的情况下直接创建应用通道。 1.1 修改配置文件 先创建配置文…...

力扣每日一题(2023-11-30)
力扣每日一题 题目:1657. 确定两个字符串是否接近 日期:2023-11-30 用时:21 m 07 s 时间:11ms 内存:43.70MB 代码: class Solution {public boolean closeStrings(String word1, String word2) {if(word1.…...