【Qt】深入探索Qt事件处理:从基础到高级自定义:QEvent

news/2024/7/22 1:30:51/文章来源:https://blog.csdn.net/Colorful___/article/details/139220213

文章目录

  • 前言:
  • 1. 事件的介绍
  • 2. 事件的处理
    • 2.1. 示例1: 重写鼠标进入和鼠标离开事件
    • 2.2. 示例2:当鼠标点击时,获取对应的坐标值;
    • 2.3. 鼠标释放事件
    • 2.4. 鼠标双击事件
    • 2.5. 鼠标移动事件
    • 2.6. 鼠标滚轮的滚动事件
  • 3. 按键事件
  • 4. 定时器事件
  • 6. 窗口移动事件
  • 总结

虽然 Qt 是跨平台的 C++ 开发框架,Qt的很多能力其实是操作系统提供的,只不过 Qt 封装了系统的 API。
程序是运行在操作系统上的,需要系统给我们支撑

  1. 事件
  2. 文件操作
  3. 多线程编程
  4. 网络编程
  5. 多媒体(音频,视频)

前言:

在现代软件开发中,用户界面的交互性是衡量软件质量的重要标准之一。Qt,作为一个功能强大的跨平台C++开发框架,提供了丰富的机制来处理用户界面的各种交互事件。本文旨在深入探讨Qt中的事件处理机制,包括鼠标、键盘、定时器以及窗口事件等,并通过具体的示例代码,展示如何在Qt应用程序中重写和处理这些事件。通过本文的学习,开发者将能够更加灵活和深入地定制用户界面的行为,提升应用程序的交互性和用户体验。

1. 事件的介绍

  • 信号槽:
    用户进行的各种操作,就可能会产生出信号。可以给某个信号指定槽函数,当信号触发时,就能够自动执行到对应的槽函数。
  • 事件非常类似
    用户的各种操作,也会产生事件。程序员同样可以给事件关联上处理函数(处理的逻辑),当事件触发的时候,就能够执行对应的代码。

事件本身是操作系统提供的机制,Qt 也同样把操作系统事件机制经行了封装,拿到了 Qt 中,但是由于事件对应的代码编写起来的代码编写起来不是很方便,Qt 对事件机制又进行了进一步的封装,就得到了信号槽。
所以,信号槽就是对于事件的进一步封装,事件是信号槽的底层机制。

实际 Qt 开发过程中,绝大部分和用户之间进行的操作都是通过“信号槽”来完成的。有些特殊情况下,信号槽不一定能搞定(某个用户的动作行为,Qt
没有提供对应的信号…)此时就需要通过重写事件处理函数的形式,来手动处理事件的响应逻辑。

开发事件机制给咱们,咱们就可以根据实际需要进行更深度的定制化的 DIY 操作了。
用户进行了很多操作,就会产生很多的事件(当然也会产生很多的信号)
在这里插入图片描述
QEvent: 事件概念
子类:代表各种具体的事件。不同场景下,要关注的点是不一样的,这些事件的子类中就会包含一些对应不同的属性。

2. 事件的处理

事件的处理一般常用的方法为:重写相关的 Event 函数
在 Qt 中,几乎所有的 Event 函数都是虚函数,所以可以重新实现。如:在实现鼠标的进入和离开事件时,直接重新实现 enterEvent()leaveEvent() 即可。

让一段代码和某个事件关联起来,当事件触发的时候,就能指定到这段代码,之前信号槽这里通过 connect 来完成上述关联的,对于事件来说还不太一样。

对于事件来收,让当前类重写某个事件处理函数(这里用到的是“多态”的机制创建子类,继承自 Qt 已有的类,在子类中重写父类的事件处理函数)
后续的事件触发过程中,就会通过多态这样的机制,执行到咱们自己写的子类的函数中。

2.1. 示例1: 重写鼠标进入和鼠标离开事件

处理一下鼠标进入和鼠标离开:enterEvent()leaveEvent()
它们都是虚函数,虚函数才能被子类重写。
在这里插入图片描述
在这里插入图片描述
边框显示出来,方便观察当前鼠标是否进去离开:
在这里插入图片描述
这里需要创建 QLabel 的子类,重写 enterEventleaveEvent
在这里插入图片描述
在这里插入图片描述

// label.h
#include <QWidget>
#include <QLabel>class Label : public QLabel
{Q_OBJECT
public:Label(QWidget* parent); // QWidget* parent 让 Label有父控件
};
// label.cpp
#include "label.h"Label::Label(QWidget* parent) : QLabel(parent)
{}

为创建的类,重写 enterEventleaveEvent这两个函数

void enterEvent(QEvent* event);
void leaveEvent(QEvent* event);

要想重写父类的函数,就需要保证你这边写的函数名字和函数的参数列表都完全一致(形参名无所谓),谨防单词拼写错误。 正常来说Qt Creator
应该要能够提示出来,但实际上没有,所以这里只能通过手动的方式从Qt文档把它复制过来了,以免拼写错误。

上述代码,虽然重写了这两函数,但是还是有点问题
在这里插入图片描述
当前界面上的这个 Label 其实是 QLable, 不是咱们自己写的 Label,必须确保界面上的这个 label 是一个咱们自己定义的 Label 类的实例,才会执行到。
在这里插入图片描述
在这里插入图片描述
一定要确保,类名和头文件名与自定义的是匹配的,一定不能有拼写错误!
在这里插入图片描述
通过“提升为”这样的方式,就可以把 Qt Designer 中拖上去的控件的类型转换成自定义的控件类型。
在这里插入图片描述

2.2. 示例2:当鼠标点击时,获取对应的坐标值;

mousePressEvent 这个函数,按下左键,右键,滚轮都能触发。
有的鼠标还带有前进后退侧键,也是可以触发。
但是还有的鼠标,有更多的按键,更多按键就不一定了

void Label::mousePressEvent(QMouseEvent *event)
{// 当前 event 对象就包含了鼠标点击位置的坐标qDebug() << event;if (event->button() == Qt::LeftButton) {qDebug() << "按下左键";} else if (event->button() == Qt::RightButton) {qDebug() << "按下右键";}qDebug() << event->x() << " , "<< event->y();// globalX 和 globalY 是以屏幕左上角为原点,获取的坐标qDebug() << event->globalX() << " , " << event->globalY();
}

在这里插入图片描述

2.3. 鼠标释放事件

鼠标释放事件是通过虚函数 mouseReleaseEvent() 来捕获的。

void Label::mouseReleaseEvent(QMouseEvent *event)
{if (event->button() == Qt::LeftButton) {qDebug() << "释放左键";} else if (event->button() == Qt::RightButton) {qDebug() << "释放右键";}
}

clicked 这样的信号,就相当于是一次鼠标按下事件和一次鼠标释放事件

2.4. 鼠标双击事件

在这里插入图片描述

当第二按下的时候,才能够识别到是“双击”
比如有的程序,可能是单击有一些逻辑,双击有另一些逻辑。
如果我们没注意,可能双击操作就能触发单击逻辑,可能就有bug

2.5. 鼠标移动事件

刚才重写鼠标的操作,都是在自定义的 Label 中完成的。此时鼠标只有在 Label 范围内进行动作的时候,才能捕捉到。
也可以把这些操作直接放到 Widget(QWidget 子类) 来完成。这样的话,鼠标在整个窗口中进行各种动作都能获取了。

// widget.h
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include <QMouseEvent>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();void mouseMoveEvent(QMouseEvent* event);private:Ui::Widget *ui;
};#endif // WIDGET_H
// widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);// 把这个选项设置为 true,才能够追踪鼠标移动的位置this->setMouseTracking(true);
}Widget::~Widget()
{delete ui;
}void Widget::mouseMoveEvent(QMouseEvent *event)
{qDebug() << event->x() << "," << event->y();
}

鼠标移动不同于鼠标按下,随便移动一下鼠标,就会产生大量的鼠标移动事件。当你进行捕获事件的时候,尤其是在这里进行一些复杂逻辑的时候,程序负担就很重,很容易产生卡顿之类的情况。
Qt 为了保证系统的流畅性,默认情况下不会对鼠标移动进行追踪,鼠标移动的时候不会调用mouseMoveEvent。除非显示告诉 Qt 就要追踪鼠标位置。

2.6. 鼠标滚轮的滚动事件

在 Qt 中,鼠标滚轮事件是通过 QWheelEvent 类来实现的。滚轮滑动的距离可以通过 delta() 函数获取。

#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QWheelEvent>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);_total = 0;
}Widget::~Widget()
{delete ui;
}void Widget::wheelEvent(QWheelEvent *event)
{_total += event->delta();qDebug() << _total;
}

3. 按键事件

Qt 中的按键事件是通过 QKeyEvent 类来实现的。当键盘上的按键被按下或者被释放时,键盘事件便会触发。
之前 QShortCut, 设置快捷键用到过键盘。这是信号槽机制封装过的,获取键盘按键的方式。
站在更底层的角度,也可以通过事件获取到当前用户键盘按下 的情况。

keyPressEvent(QKeyEvent*); 

看你捕获键盘是在哪个控件范围内

void Widget::keyPressEvent(QKeyEvent *event)
{
//    qDebug() << event;
//    qDebug() << event->key();if (event->key() == Qt::Key_A) {qDebug() << "按下了 A 键";}
}

如果是组合键呢?

void Widget::keyPressEvent(QKeyEvent *event)
{
//    qDebug() << event;
//    qDebug() << event->key();if (event->key() == Qt::Key_A && event->modifiers() == Qt::ControlModifier) {qDebug() << "按下了 ctrl + A 键";}
}

4. 定时器事件

QTimer 实现了定时器的功能,在QTimer 背后是 QTimerEvent 定时器事件进行支撑的。
QObject 提供了一个 timerEvent 这个函数。
startTimer 启动定时器
killTimer 关闭定时器

次数 timerID 类似于 Linux 上的“文件描述符”, 起到身份标识效果。

#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);// 开启定时器事件// 此处 timerID 是一个定时器的身份标识_timerID = this->startTimer(1000);
}Widget::~Widget()
{delete ui;
}void Widget::timerEvent(QTimerEvent *event)
{// 如果一个程序中存在多个定时器(startTimer 创建的定时器),此时每个定时器都会触发 timerEvent 函数// 先判定一下这次触发是否是想要的定时器触发的if (event->timerId() != this->_timerID) {// 如果不是我们的定时器触发的,就直接忽略// 当前程序中只有这一个定时器return;}// 是咱们自己搞的定时器int value = ui->lcdNumber->intValue();if (value <= 0) {// 停止定时器this->killTimer(this->_timerID);return;}value -= 1;ui->lcdNumber->display(value);
}

使用 timerEventQTimer 还是要更复杂一点。手动管理 timerID, 还要区分这次函数调用是哪个 timer 引起,后续还是使用 QTimer 即可。

6. 窗口移动事件

moveEvent 窗口移动时触发的事件。
resizeEvent 窗口大小改变时触发的事件。

void Widget::moveEvent(QMoveEvent *event)
{qDebug() << event->pos();
}void Widget::resizeEvent(QResizeEvent *event)
{qDebug() << event->size();
}

事件分发 / 事件过滤,属于 Qt 事件机制背后的一些逻辑,Qt 也把这部分内容提供一些 API 让程序员有更多的可操作空间。
事件分发:重写 event 函数,直接获取到所有的事件(杀伤力比较广,不当使用可能对现有的逻辑(现有的事件体系造成一些负面影响))。
当然,有些场景中,比如要禁用用户的某些操作,可以考虑使用事件过滤器机制。
最后还要要优先使用信号槽机制,因为这都是Qt给我们封装好了让我们去使用的。

总结

本文详细介绍了Qt框架中的事件处理机制,包括事件的基本概念、事件的处理方法以及具体的事件类型和处理示例。我们了解到,Qt通过封装操作系统的事件机制,提供了信号槽这一更加方便的事件处理方式。然而,在某些特殊场景下,我们可能需要通过重写事件处理函数来进行更深层次的自定义。文中通过鼠标事件、按键事件、定时器事件以及窗口事件等多个方面的示例,展示了如何在Qt中实现这些事件的处理。

我们学习了如何通过重写enterEvent()leaveEvent()来处理鼠标的进入和离开事件,如何通过mousePressEvent()mouseReleaseEvent()来捕获鼠标的点击和释放,以及如何通过mouseMoveEvent()wheelEvent()来追踪鼠标的移动和滚轮的滚动。此外,还探讨了按键事件的捕获和处理,定时器事件的启动和停止,以及窗口移动和大小改变事件的处理。

最后,文章强调了在使用事件处理机制时,应当优先考虑Qt提供的信号槽机制,因为它更为简洁和安全。但在需要更细致控制的场景下,事件处理函数的重写和事件过滤机制提供了更多的灵活性。通过本文的学习,开发者应该能够更加熟练地运用Qt的事件处理机制,创建出更加丰富和响应灵敏的用户界面。

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

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

相关文章

Android11 事件分发流程

在Android 11 输入系统之InputDispatcher和应用窗口建立联系一文中介绍到&#xff0c;当InputDispatcher写入数据后&#xff0c;客户端这边就会调用handleEvent方法接收数据 //frameworks\base\core\jni\android_view_InputEventReceiver.cpp int NativeInputEventReceiver::h…

HTML静态网页成品作业(HTML+CSS)——宠物狗介绍网页(3个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;未使用Javacsript代码&#xff0c;共有3个页面。 二、作品演示 三、代…

网页图片加载慢的求解指南

网页/图片加载慢的求解指南 一、前言与问题描述 今天刚换上华为的HUAWEI AX3 Pro New&#xff0c;连上WIFI后测速虽然比平时慢&#xff0c;但是也不算太离谱&#xff0c;如下图所示&#xff1a; 估计读者们有也和作者一样&#xff0c;还没意识到事情的严重性&#x1f601;。 …

452. 用最少数量的箭引爆气球(中等)

452. 用最少数量的箭引爆气球 1. 题目描述2.详细题解3.代码实现3.1 Python3.2 Java 1. 题目描述 题目中转&#xff1a;452. 用最少数量的箭引爆气球 2.详细题解 引爆所有气球&#xff0c;弓箭数要最少&#xff0c;那么每支弓箭尽量多的引爆气球&#xff0c;采用贪心策略。对于…

用源码建站可能涉及知产侵权,建站的注意!

近日普推知产老杨看到央视报道一家公司用了某建站源码涉及知产侵权&#xff0c;起诉了全国八千多家公司&#xff0c;某梦自从创始人因病转给某公司后&#xff0c;也在大量起诉用其建站代码公司侵权&#xff0c;他们也都是申请了相关的著作权。 有的中小企业在运营中会涉及建站…

Kibana(一张图片胜过千万行日志)

Kibana&#xff08;一张图片胜过千万行日志&#xff09; Kibana是一个开源的分析和可视化平台&#xff0c;设计用于和Elasticsearch一起工作。 你用Kibana来搜索&#xff0c;查看&#xff0c;并和存储在Elasticsearch索引中的数据进行交互。 你可以轻松地执行高级数据分析&a…

STM32H750外设之ADC通道选择

目录 概述 1 通道选择功能介绍 2 通道选择&#xff08; SQRx、 JSQRx&#xff09; 2.1 通道复用 2.1.1 通道介绍 2.1.2 通道框图 2.2 转换分组 2.3 内部专用通道 3 通道预选寄存器 (ADCx_PCSEL) 3.1 功能介绍 3.2 预选通道寄存器 概述 本位主要介绍STM32H750外设之…

07_Servlet

Servlet 一 Servlet简介 1.1 动态资源和静态资源 静态资源 无需在程序运行时通过代码运行生成的资源,在程序运行之前就写好的资源. 例如:html css js img ,音频文件和视频文件 动态资源 需要在程序运行时通过代码运行生成的资源,在程序运行之前无法确定的数据,运行时动态生成…

2种方法将集合数据List构建成树形结构

文章目录 递归循环构建树结构hutool.TreeUtil.build构建树结构 递归循环构建树结构 先查最外层树节点数据&#xff0c;再递归遍历每一层子节点数据 public ApiResultDto<List<LocationDto>> getTreeByParams(LocationSearchDto searchDto, SecurityUser user) {// …

Python Keras:打造深度学习模型的利器

更多Python学习内容&#xff1a;ipengtao.com Keras是一个高级神经网络API&#xff0c;由纯Python编写并能够运行在TensorFlow、Theano和CNTK之上。Keras以简洁和可扩展性为目标&#xff0c;使研究人员能够快速实验&#xff0c;并能在生产环境中轻松部署。无论是深度学习的新手…

智慧水坝:科技变革的里程碑

在曾经的水利工程领域&#xff0c;水坝只是为了水资源的调配和控制&#xff0c;提供一定的安全储备。然而&#xff0c;随着现代科技的不断发展&#xff0c;传统的水坝已经不再是单一的水源控制工程&#xff0c;而是变成了一个充满智慧与创新的生态系统。智慧水坝的概念已经超越…

【重制版】Unity Meta Quest 一体机开发(一):前期准备,Meta XR SDK导入和环境配置,配置玩家物体

文章目录 &#x1f4d5;教程说明&#x1f4d5;Meta XR SDK 介绍&#x1f4d5;前期准备⭐开启开发者模式⚡在 Meta 官网申请开发者⚡在 Meta Quest 手机 APP 开启开发者 ⭐电脑需要下载的软件⚡Meta Quest Link&#xff08;以前叫做Oculus PC客户端&#xff09;⚡Oculus ADB Dri…

工业级3D开发引擎HOOPS:创新与效率的融合!

在当今这个技术日新月异的时代&#xff0c;3D技术已成为推动各行各业发展的重要力量。从工程设计到游戏开发&#xff0c;从虚拟现实到增强现实&#xff0c;3D技术的应用无处不在&#xff0c;它极大地丰富了我们的生活和工作。而在这样的背景下&#xff0c;HOOPS作为一个强大的3…

想玩转Python爬虫?这些知识点你必须掌握!

想玩转Python爬虫&#xff1f;这些知识点你必须掌握&#xff01; Python爬虫作为数据获取的利器&#xff0c;吸引着越来越多的人学习。但想要真正掌握这门技术&#xff0c;仅仅停留在“会用”的阶段是远远不够的。你需要建立一个系统的知识体系&#xff0c;才能在面对各种复杂情…

景源畅信:如何让抖音短视频标签越来越精准?

在数字媒体的海洋中&#xff0c;抖音短视频以其独特的魅力和高效的信息传递方式吸引了无数眼球。如何让这些短视频的标签变得更精准&#xff0c;是提高推荐质量、增强用户体验的关键问题。标签的精确度直接关系到内容能否触达感兴趣的用户&#xff0c;进而影响用户的留存率和平…

实现 Vue 标签页切换效果的组件开发

在本次开发中&#xff0c;我们将实现一个 Vue 组件&#xff0c;用于展示和切换标签页。 背景有移动动画效果 该组件将具有以下功能&#xff1a; 标签页左右滚动点击标签页切换内容关闭指定标签页支持多种标签页风格 以下是实现该组件的具体步骤&#xff1a; 创建 Vue 组件…

stream-并行流

定义 常规的流都是串行的流并行流就是并发的处理数据&#xff0c;一般要求被处理的数据互相不影响优点&#xff1a;数据多的时候速度更快&#xff0c;缺点&#xff1a;浪费系统资源&#xff0c;数据少的时候开启线程更耗费时间 模版 Stream<Integer> stream1 Stream.of…

Redis篇 String

String概念和set,get扩充 一. String类型的基本介绍二. String中set,get方法扩充 一. String类型的基本介绍 redis中所有的key都是字符串类型的,但是value的类型差异很大. redis中的字符串,直接就是二进制方式存储的,可以存储整数,二进制数据 文本数据,Json,xml还有音频等. 二.…

Unity 生成物体的几种方式

系列文章目录 unity工具 文章目录 系列文章目录前言&#x1f449;一、直接new的方式创建生成1-1.代码如下1-2. 效果图 &#x1f449;二、使用Instantiate创建生成&#xff08;GameObject&#xff09;2-1.代码如下2-2.效果如下图 &#x1f449;三.系统CreatePrimitive创建生成3…

C++ | Leetcode C++题解之第116题填充每个节点的下一个右侧节点指针

题目&#xff1a; 题解&#xff1a; class Solution { public:Node* connect(Node* root) {if (root nullptr) {return root;}// 从根节点开始Node* leftmost root;while (leftmost->left ! nullptr) {// 遍历这一层节点组织成的链表&#xff0c;为下一层的节点更新 next…