Qt 的事件投递机制:从基础到实战

在 Qt 开发中,事件系统是核心概念之一,几乎每一个 GUI 应用程序都依赖于它来响应用户操作和系统通知。对于有一定 Qt 基础但首次接触事件投递 (QCoreApplication::postEvent) 的开发者而言,理解事件投递机制尤为重要。这篇博客将带你从基本概念到实际应用,逐步掌握 Qt 的事件投递机制。


目录

    • 1. 事件系统基础
      • 事件的三种常见使用方式
    • 2. 什么是事件投递?
      • 事件投递的优点
    • 3. 事件投递的基本流程
      • 3.1 定义自定义事件类
      • 3.2 接收事件的类
      • 3.3 投递事件
    • 4. 事件投递的运行原理
      • 4.1 事件队列
      • 4.2 事件处理
    • 5. 应用场景
    • 6. 完整代码示例
      • 主函数
      • 自定义事件类
      • 事件接收类
    • 7. 注意事项
    • 8. 总结


1. 事件系统基础

Qt 的事件系统 (QEvent) 是用来通知对象某些操作发生的机制。常见的事件类型包括:

  • 用户交互事件:鼠标点击 (QMouseEvent)、键盘输入 (QKeyEvent)。
  • 窗口管理事件:绘制请求 (QPaintEvent)、窗口大小变化。
  • 自定义事件:用于特定应用场景的事件类型。

事件的三种常见使用方式

  1. 信号与槽机制:Qt 提供的内置通信机制,适用于大多数场景。
  2. 重写事件处理函数:如 mousePressEvent,用于处理特定事件。
  3. 自定义事件并通过 postEvent 投递:适用于需要异步处理或跨线程通信的高级场景。

2. 什么是事件投递?

事件投递是一种将事件异步发送到目标对象的机制,使用 QCoreApplication::postEvent 方法可以将事件添加到目标对象的事件队列中,等待其在事件循环中被处理。

事件投递的优点

  • 异步执行:发送事件后,调用者无需等待事件处理完成,提升程序响应性。
  • 线程安全:允许在不同线程之间安全地传递事件,简化跨线程通信。
  • 高效灵活:适用于需要模块解耦或复杂消息传递的场景。

3. 事件投递的基本流程

理解事件投递机制需要掌握以下几个步骤:

3.1 定义自定义事件类

首先,定义一个继承自 QEvent 的自定义事件类,用于携带特定的数据。

// ImageProcessEvent.h
#pragma once
#include <QEvent>
#include <QByteArray>class ImageProcessEvent : public QEvent {
public:static const QEvent::Type Type; // 自定义事件类型ImageProcessEvent(int width, int height, int depth, const QByteArray &data): QEvent(Type), width(width), height(height), depth(depth), data(data) {}int getWidth() const { return width; }int getHeight() const { return height; }int getDepth() const { return depth; }QByteArray getData() const { return data; }private:int width, height, depth;QByteArray data;
};
// ImageProcessEvent.cpp
#include "ImageProcessEvent.h"const QEvent::Type ImageProcessEvent::Type = static_cast<QEvent::Type>(QEvent::User + 1);

说明

  • Type 是自定义事件类型,需在 .cpp 文件中定义,通常从 QEvent::User 开始自定义类型编号。
  • 事件类封装了图像处理所需的数据,如宽度、高度、深度和数据缓冲区。

3.2 接收事件的类

目标类需要继承自 QObject 并重写 event 函数,以处理自定义事件。

// ImageProcess.h
#pragma once
#include <QObject>
#include "ImageProcessEvent.h"class ImageProcess : public QObject {Q_OBJECT
public:explicit ImageProcess(QObject *parent = nullptr) : QObject(parent) {}protected:bool event(QEvent *e) override {if (e->type() == ImageProcessEvent::Type) {auto *event = static_cast<ImageProcessEvent *>(e);// 处理图像事件process(event->getWidth(), event->getHeight(), event->getDepth(), event->getData());return true;  // 事件已处理}return QObject::event(e);  // 调用基类处理其他事件}private:void process(int width, int height, int depth, const QByteArray &data) {// 实际的图像处理逻辑qDebug() << "Processing image of size:" << width << "x" << height;// 例如,可以将数据转换为 OpenCV Mat 进行进一步处理}
};

说明

  • 重写 event 函数以拦截和处理特定类型的事件。
  • process 函数中实现实际的图像处理逻辑。

3.3 投递事件

在适当的位置使用 QCoreApplication::postEvent 将事件投递到目标对象。

// Example usage
#include <QCoreApplication>
#include "ImageProcess.h"
#include "ImageProcessEvent.h"void udpImageCallback(ImageProcess *imageProcess) {int width = 1920, height = 1080, depth = 14;QByteArray data = QByteArray::fromRawData("dummy_data", 10);// 创建事件对象auto *event = new ImageProcessEvent(width, height, depth, data);// 异步投递到 ImageProcessQCoreApplication::postEvent(imageProcess, event);
}

说明

  • 使用 new 动态分配事件对象,Qt 会在事件处理完成后自动销毁它。
  • udpImageCallback 模拟了从 UDP 接收数据并投递事件的场景。

4. 事件投递的运行原理

理解事件投递的运行机制有助于更高效地应用它。

4.1 事件队列

  • 每个 QObject 子类都有一个独立的事件队列。
  • 调用 QCoreApplication::postEvent 时,事件被添加到目标对象的事件队列中。

4.2 事件处理

  • Qt 的事件循环 (QEventLoop) 持续从事件队列中取出事件并分发给相应的对象。
  • 当事件被分发到目标对象时,Qt 会调用该对象的 event 函数。
  • 如果 event 函数处理了事件(返回 true),事件循环将停止进一步传播该事件。
  • 否则,事件会传递给基类的 event 函数,允许基类处理或忽略事件。

5. 应用场景

事件投递机制在多种场景下非常有用,尤其是涉及异步处理或跨线程通信时。

  1. 跨线程通信

    • 将事件从后台线程发送到主线程以更新 UI。
    • 处理耗时任务后,将结果事件发送回主线程。
  2. 模块解耦

    • 将逻辑处理与界面渲染分离,通过事件传递数据,减少模块间的依赖。
  3. 复杂任务调度

    • 在不阻塞主线程的情况下,异步处理任务,提高应用响应性。

6. 完整代码示例

下面是一个完整的示例,展示如何定义自定义事件、处理事件并投递事件。

主函数

// main.cpp
#include <QCoreApplication>
#include "ImageProcess.h"
#include "ImageProcessEvent.h"// 模拟的 UDP 回调函数
void udpImageCallback(ImageProcess *imageProcess) {int width = 1920, height = 1080, depth = 14;QByteArray data = QByteArray::fromRawData("dummy_data", 10);// 创建事件对象auto *event = new ImageProcessEvent(width, height, depth, data);// 异步投递到 ImageProcessQCoreApplication::postEvent(imageProcess, event);
}int main(int argc, char *argv[]) {QCoreApplication app(argc, argv);ImageProcess imageProcess;// 模拟数据接收并投递事件udpImageCallback(&imageProcess);return app.exec();
}

自定义事件类

// ImageProcessEvent.h
#pragma once
#include <QEvent>
#include <QByteArray>class ImageProcessEvent : public QEvent {
public:static const QEvent::Type Type; // 自定义事件类型ImageProcessEvent(int width, int height, int depth, const QByteArray &data): QEvent(Type), width(width), height(height), depth(depth), data(data) {}int getWidth() const { return width; }int getHeight() const { return height; }int getDepth() const { return depth; }QByteArray getData() const { return data; }private:int width, height, depth;QByteArray data;
};
// ImageProcessEvent.cpp
#include "ImageProcessEvent.h"const QEvent::Type ImageProcessEvent::Type = static_cast<QEvent::Type>(QEvent::User + 1);

事件接收类

// ImageProcess.h
#pragma once
#include <QObject>
#include <QDebug>
#include "ImageProcessEvent.h"class ImageProcess : public QObject {Q_OBJECT
public:explicit ImageProcess(QObject *parent = nullptr) : QObject(parent) {}protected:bool event(QEvent *e) override {if (e->type() == ImageProcessEvent::Type) {auto *event = static_cast<ImageProcessEvent *>(e);// 处理图像事件process(event->getWidth(), event->getHeight(), event->getDepth(), event->getData());return true;  // 事件已处理}return QObject::event(e);  // 调用基类处理其他事件}private:void process(int width, int height, int depth, const QByteArray &data) {// 实际的图像处理逻辑qDebug() << "Processing image of size:" << width << "x" << height;// 例如,可以将数据转换为 OpenCV Mat 进行进一步处理}
};

7. 注意事项

  1. 事件生命周期

    • 使用 new 动态分配事件对象,Qt 会在事件处理完成后自动销毁它。
    • 避免在栈上创建事件对象,以防止内存泄漏或访问已释放内存。
  2. 事件优先级

    • 高优先级事件会被优先处理。
    • 使用 QCoreApplication::sendEvent 可以同步处理事件,但可能导致调用者阻塞,影响性能。
  3. 线程安全

    • 虽然事件投递本身是线程安全的,但事件处理逻辑需要确保线程安全,避免竞态条件和数据不一致。
  4. 事件类型冲突

    • 确保自定义事件类型不会与 Qt 内置事件类型冲突,通常从 QEvent::User 开始自定义。

8. 总结

Qt 的事件投递机制 (QCoreApplication::postEvent) 提供了一种高效、灵活的异步通信方式,特别适用于跨线程操作和模块解耦的场景。通过自定义事件类、重写 event 函数以及合理地投递事件,开发者可以轻松实现复杂的消息传递逻辑。


参考资料

  • Qt Documentation - Event Handling
  • Qt Documentation - QCoreApplication::postEvent

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

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

相关文章

QDialog中,reject()和close()区别

1. reject()函数 reject()是QDialog类中的一个槽函数&#xff0c;用于以“拒绝”的方式关闭对话框。它通常与对话框的“取消”操作相关联。当调用reject()时&#xff0c;会发出rejected()信号。 行为细节&#xff1a; 从模态对话框的角度来看&#xff0c;当模态对话框调用reje…

【036】基于51单片机五子棋游戏机【Proteus仿真+Keil程序+报告+原理图】

☆、设计硬件组成&#xff1a;51单片机最小系统LCD12864液晶显示按键控制。 1、设计采用STC89C51/52、AT89C51/52、AT89S51/52作为主控芯片&#xff0c;采用LCD12864液晶作为显示&#xff1b; 2、游戏有 人机对战 和 玩家对战 两种模式&#xff0c;玩家白子先下&#xff1b; …

HTML通过JavaScript获取访问连接,IP和端口

<!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <title>Get IP Address</title> <script> function displayURL() { var url window.location.href; // 获取当…

VMWARE虚拟交换机的负载平衡算法

一、基于源虚拟端口的路由 虚拟交换机可根据 vSphere 标准交换机或 vSphere Distributed Switch 上的虚拟机端口 ID 选择上行链路。 基于源虚拟端口的路由是 vSphere 标准交换机和 vSphere Distributed Switch 上的默认负载平衡方法。 ESXi主机上运行的每个虚拟机在虚拟交换…

slam里的体素滤波

SLAM系统通常需要处理大量的传感器数据&#xff0c;如激光雷达&#xff08;LiDAR&#xff09;、相机等获取的数据&#xff0c;这些数据往往包含了大量的冗余信息和噪声。为了提高SLAM系统的效率和准确性&#xff0c;数据预处理是非常重要的一步&#xff0c;体素滤波就是一种常用…

web——sqliabs靶场——第十二关——(基于错误的双引号 POST 型字符型变形的注入)

判断注入类型 a OR 1 1# 发现没有报错 &#xff0c;说明单引号不是闭合类型 测试别的注入条件 a) OR 1 1# a)) OR 1 1# a" OR 11 发现可以用双引号闭合 发现是")闭合 之后的流程还是与11关一样 爆破显示位 先抓包 是post传参&#xff0c;用hackbar来传参 unam…

AI时代,百度的三大主义

现实主义、长期主义、理想主义。 定焦One&#xff08;dingjiaoone&#xff09;原创 作者 | 苏琦 郑浩钧 编辑 | 魏佳 “人工智能很像是一次新的工业革命&#xff0c;这意味着它不会三五年就结束&#xff0c;也不会一两年就出现‘超级应用’&#xff0c;它更像是三五十年对于整…

C++基础入门篇

C入门 第一个C程序 首先C兼容c语言&#xff0c;所以由c语言实现的内容仍然可以在C中实现&#xff0c;但是c语言的文件后缀是.c但是C的后缀是.cpp。vs对于cpp文件使用C编译器编译&#xff0c;linux需要用g编译而不是gcc #include<stdio> int main() {printf("hello…

基于YOLOv11的火焰实时检测系统(python+pyside6界面+系统源码+可训练的数据集+也完成的训练模型)

100多种【基于YOLOv8/v10/v11的目标检测系统】目录&#xff08;pythonpyside6界面系统源码可训练的数据集也完成的训练模型 摘要&#xff1a; 本文提出了一种基于YOLOv11算法的火灾检测系统&#xff0c;利用1852张图片&#xff08;1647张训练集&#xff0c;205张验证集&#…

Python入门(10)--面向对象进阶

Python面向对象进阶 &#x1f680; 1. 继承与多态 &#x1f504; 1.1 继承基础 class Animal:def __init__(self, name, age):self.name nameself.age agedef speak(self):passdef describe(self):return f"{self.name} is {self.age} years old"class Dog(Anim…

算法——两两交换链表中的节点(leetcode24)

这是一道对于链表节点进行操作的题目非常考验对于链表操作的基本功&#xff1b; 解法: 本题的解法结合下图来进一步解释 创建一个虚拟节点指向头结点以便使代码逻辑看起来更为简便且操作节点容易,定义cur是为了方便找到cur之后的两个节点进行交换操作定义pre和aft是为了保存执…

【提效工具开发】管理Python脚本执行系统实现页面展示

Python脚本执行&#xff1a;工具管理Python脚本执行系统 背景 在现代的软件开发和测试过程中&#xff0c;自动化工具和脚本的管理变得至关重要。为了更高效地管理工具、关联文件、提取执行参数并支持动态执行Python代码&#xff0c;我们设计并实现了一套基于Django框架的工具…

鸿蒙开发:ForEach中为什么键值生成函数很重要

前言 在列表组件使用的时候&#xff0c;如List、Grid、WaterFlow等&#xff0c;循环渲染时都会使用到ForEach或者LazyForEach&#xff0c;当然了&#xff0c;也有单独使用的场景&#xff0c;如下&#xff0c;一个很简单的列表组件使用&#xff0c;这种使用方式&#xff0c;在官…

Figma插件指南:12款提升设计生产力的插件

在当今的设计领域&#xff0c;Figma已经成为许多UI设计师和团队的首选原型和数字设计软件。随着Figma的不断更新和插件库的扩展&#xff0c;这些工具极大地提升了设计工作的效率。本文将介绍12款实用的Figma插件&#xff0c;帮助你在UI设计中更加高效。 即时AI 即时AI利用先进…

揭秘云计算 | 5、关于云计算效率的讨论

一、 公有云效率更高&#xff1f; 解&#xff1a;公有云具有更高的效率。首先我们需要知道效率到底指的是什么。这是个亟须澄清的概念。在这里效率是指云数据中心&#xff08;我们将在后文中介绍其定义&#xff09;中的IT设备资源利用率&#xff0c;其中最具有代表性的指标就是…

Spring Boot 和 Spring Cloud 构建一个完整的微服务架构——在线购物系统

接上一篇博客&#xff0c;大家可以结合一起看看实例理解https://blog.csdn.net/speaking_me/article/details/143917383?spm1001.2014.3001.5502 构建一个综合性的大型微服务项目可以帮助开发者更全面地理解和掌握 Spring Boot 和 Spring Cloud 的应用。 接下来&#xff0c;…

Flutter:AnimatedPadding动态修改padding

// 默认top为10&#xff0c;点击后修改为100&#xff0c;此时方块会向下移动 padding: EdgeInsets.fromLTRB(left, top, right, bottom),class _MyHomePageState extends State<MyHomePage> {bool flag true;overrideWidget build(BuildContext context) {return Scaffo…

【c++丨STL】stack和queue的使用及模拟实现

&#x1f31f;&#x1f31f;作者主页&#xff1a;ephemerals__ &#x1f31f;&#x1f31f;所属专栏&#xff1a;C、STL 目录 前言 一、什么是容器适配器 二、stack的使用及模拟实现 1. stack的使用 empty size top push和pop swap 2. stack的模拟实现 三、queue的…

JavaEE初学07

JavaEE初学07 MybatisORMMybatis一对一结果映射一对多结果映射 Mybatis动态sqlif标签trim标签where标签set标签foreach标签补充 在这里插入图片描述右击运行即可 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/c71d44d027374a399d5d537ce96f00e1.png) Mybatis Myba…

《Spring Cloud 微服务架构探秘》

一、Spring Cloud 微服务架构概述 Spring Cloud 是基于 Spring Boot 构建的微服务开发框架&#xff0c;它充分利用了 Spring Boot 的便利性&#xff0c;极大地简化了分布式系统基础设施的开发。 Spring Cloud 具有诸多显著特点。首先&#xff0c;它提供了丰富的组件&#xff0…