【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, QKeyEventQCloseEvent。每个类都是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),事件将被传播到接收者的父对象,依此类推直到顶级对象。
处理事件有五种不同的方式;重新实现这个虚函数只是其中之一。下面列出了所有五种方法:

  1. 重新实现了paintEvent(), mousePressEvent()等。这是最常见、最简单、也是最不强大的方法。
  2. 重新实现这个函数notify()。这是非常强大的,提供完全的控制;但是一次只能有一个子类处于活动状态。
  3. 在QCoreApplication::instance()上安装事件过滤器。这样的事件过滤器能够处理所有小部件的所有事件,因此它与重新实现notify()一样强大;此外,可以有多个应用程序全局事件过滤器。全局事件过滤器甚至可以查看禁用小部件的鼠标事件。请注意,应用程序事件筛选器仅对位于主线程中的对象调用。
  4. 重新实现QObject::event()(与QWidget一样)。如果这样做,可以按Tab键,并且可以在任何特定于小部件的事件过滤器之前看到事件。
  5. 在对象上安装事件筛选器。这样的事件过滤器获取所有事件,包括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()事件
  • 进入目标对象的具体处理事件
  • 进入父对象的事件

流程图如下:
在这里插入图片描述

结论

天没降大任于我,照样苦我心智,劳我筋骨

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/190804.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MATLAB算法实战应用案例精讲-【图像处理】数字图像处理(补充篇)

目录 算法原理 数字图像处理常用算法 1.二值化: 2.海报化 3.灰度化

基于ZLMediaKit的webrtc实时视频传输demo搭建

环境 ubuntu 20.04 ​ gcc version 9.4.0 ​ cmake version 3.16.3 部署ZLMediaKit流媒体服务器 安装openssl 首先可以检查一下自己的openssl的版本如果是1.1.1以上就可以忽略这一步 wget https://www.openssl.org/source/openssl-1.1.1k.tar.gz tar -xvzf openssl-1.1.1k…

【新论文】【模型攻击】DiffAttack 针对基于扩散的对抗性净化的逃避攻击

DiffAttack: Evasion Attacks Against Diffusion-Based Adversarial Purification 作者: Mintong Kang; Dawn Song; Bo Li 链接: http://arxiv.org/pdf/2311.16124v1 备注: Accepted to NeurIPS 2023 摘要: 基于扩散的净化防御利用扩散模型去除对抗样本的精心设计的扰动&#…

酷开科技 | 酷开系统,让家庭娱乐方式焕然一新!

在这个快节奏的社会&#xff0c;家庭娱乐已成为我们日常生活中不可或缺的一部分&#xff0c;为了给家庭带来更多欢笑与感动&#xff0c;酷开科技发力研发出拥有丰富内容和技术的智能电视操作系统——酷开系统&#xff0c;它集合了电影、电视剧、综艺、游戏、音乐等海量内容&…

我的2023年12月02日对文章发送的一个测试

1 Markdown.com.cn 简介 支持自定义样式的 Markdown 编辑器支持微信公众号、知乎和稀土掘金点击右上方对应图标&#xff0c;一键复制到各平台 2 Markdown语法教程 2.1 标题 不同数量的#可以完成不同的标题&#xff0c;如下&#xff1a; 一级标题 二级标题 三级标题 2.2…

C语言-指针_01

指针基础 1. 概述 地址编号&#xff1a;计算机为了存储数据&#xff0c;每一个程序在 32位 机中 占4G&#xff0c;最小操作单位 是 一个字节&#xff0c;每一个字节都有其对应的地址&#xff0c;该地址就是 地址编号。 指针&#xff1a;地址编号这个数据 的 数据类型。 指针变…

Java数组与List互换

asList():将数组转成list //将数组转换为listint[][] nums {{7,0},{4,4},{7,1},{5,0},{6,1},{5,2}};List<int[]> list new LinkedList<>(Arrays.asList(nums));for (int[] ints :list) {System.out.println(ints[0] " " ints[1]); //遍历list}toAr…

TPC通信-BS架构

BS架构-基本原理 BS框架基本原理 使用线程池对BS架构进行优化

docker部署typecho博客

文章目录 1.安装git2.安装compose3.拉取仓库4.创建目录5.配置文件修改6.启动容器7.修改MYSQL数据库8.安装成功9.参考GitHub文档 1.安装git 安装git yum -y install git2.安装compose &#xff08;docker安装参考&#xff1a;docker基本知识&#xff09; 确保已经安装了 Doc…

爬虫学习-基础(HTTP原理)

目录 一、URL和URI 二、HTTP和HTTPS &#xff08;1&#xff09;HTTP &#xff08;2&#xff09;HTTPS &#xff08;3&#xff09;HTTP与HTTPS区别 &#xff08;4&#xff09;HTTPS对HTTP的改进&#xff1a;双问的身份认证 三、TCP协议 &#xff08;1&#xff09;TCP三次握手…

⭐ Unity 里让 Shader 动画在 Scene 面板被持续刷新

写 Unity Shader的时候&#xff0c;只有播放状态下的 Game 面板能看到Shader 顺畅的动态效果&#xff0c;不方便。 想要带有动态效果的 Shader 在 Scene 面板持续更新动画&#xff0c;只需要打开一个开关就能让 Scene 持续刷新动画了。 感谢大家的观看&#xff0c;您的点赞和关…

android 13.0 launcher3中workspace app列表页不显示某个app图标

1.概述 在13.0的系统ROM定制化开发中,Launcher3 workspace的app列表页 会负责加载系统中app的所有图标 但针对某个不需要显示在桌面的app图标需要过滤掉 所以需要在加载和更新的时候过滤 需要更改两处地方, 一处是 加在列表时 一处是安装卸载app 更新app列表时,接下来具体分…

learn2learn环境配置(2023年12月)

learn2learn是元学习方向的一个非常实用的库&#xff0c;但其发布时间较早&#xff0c;与最新版本的pytorch可能存在一些兼容性问题&#xff0c;在2023年12月这个时间进行安装时会遇到一些问题&#xff0c;以下是我遇到的问题及解决的方法。 1. 在我第一次直接配置“pip instal…

Cpp之旅(学习笔记)第9章 标准库

C之旅&#xff08;学习笔记&#xff09;第9章 标准库 当无知稍纵即逝时&#xff0c;又何必浪费时间学习呢&#xff1f; ——霍布斯 9.1 引言 第9~18章将对重要的标准库工具和方法给出一个概要性的介绍。如&#xff1a;string、ostream、variant、vector、map、path、unique_p…

在oracle中的scn技术

SCN可以说是Oracle中一个很基础的部分&#xff0c;但同时它也是一个很重要的。它是系统中维持数据的一致性和顺序恢复的重要标志&#xff0c;是数据库非常重要的一种数据结构。 转载&#xff1a;深入剖析 - Oracle SCN机制详细解读 - 知乎 (zhihu.com)https://zhuanlan.zhihu.…

跟我学c++高级篇——动态反射之一遍历

一、动态反射 前面讲一篇静态反射&#xff0c;今天在这个基础上对动态反射进行一下入门。动态反射前面提到过&#xff0c;一般是指在运行时动态获取类型或者生成实例。那么如何才能动态获得类型呢&#xff1f;方法有很多种&#xff0c;下面从最简单的开始。 二、入门程序 动…

基于运算放大器的电压采集电路

一、运算放大器 运放推导的两个重要概念&#xff1a;虚短、虚断。 1、差分放大器 以差分放大器为例进行推导分析。 虚断–运放的"-“端、”“端的引脚电流接近为0&#xff1b; 根据基尔霍夫电流定律可知&#xff1a;iR1iRF&#xff0c;iR2iR3&#xff1b; iR1(Ui1-(V-…

C语言结构体详解(一)(能看懂文字就能明白系列)

&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f;个人主页&#xff1a; 古德猫宁- &#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f;…

简单聊聊更快、更稳、更高效的 QUIC 协议

目录 QUIC的起源和发展 QUIC 与 TCP 和 UDP 的比较 QUIC 的主要特性 QUIC 的工作原理 QUIC 的应用场景 小结 QUIC&#xff08;Quick UDP Internet Connections&#xff0c;快速 UDP 互联网连接&#xff09;是一种基于 UDP 协议的传输层协议&#xff0c;由 Google 首次提出…

上个班而已

习惯性刷CSDN&#xff0c;发现了这么个主题&#xff0c;有意思。在我的字典里&#xff0c;“养生”这个条目已经被我删了。看过了太多的伪科学、贩卖焦虑、带货、自以为是&#xff0c;干脆眼不见为净。但程序员人均亚健康这句话真未必是个玩笑&#xff0c;所以还是凑个热闹聊上…