Qt的事件循环机制源码分析

 Qt 使用了一个事件模型来与操作系统沟通,处理诸如鼠标点击、键盘输入等用户动作。以下步骤详细解释了从操作系统捕捉鼠标点击事件到 Qt 处理该事件的过程:

1、事件处理过程

1.1、Qt 的事件循环

Qt 应用程序运行时会启动一个事件循环(QEventLoop)。该事件循环不断查询操作系统的消息队列以检查是否有新事件。一旦检测到鼠标点击事件,Qt 的应用程序框架从操作系统的消息队列中取得这个消息,并将其转换为 Qt 内部的事件格式(比如 QMouseEvent)。

1.2、 事件传递给目标窗口

在 Qt 中,每个窗口部件(QWidget)都是一个可能的事件目标。Qt 根据鼠标点击的位置确定具体的 QWidget,并将 QMouseEvent 发送到这个部件。这个部件通常是一种 QPushButton,QLineEdit 等,取决于鼠标点击的具体区域。

1.3、 事件过滤器的应用

事件可以被安装了事件过滤器的对象捕获。如果按钮等对象有事件过滤器(通过 QObject::installEventFilter() 安装),这些过滤器有机会在事件被目标 widget 处理之前预处理这个事件。如果事件过滤器决定接受(而不是忽略)该事件,则这个事件至此结束,不会被传递到原本的目的地。

1.4、 事件的处理

如果事件未被过滤器消耗,那么它将被送到目标 QWidget 进行处理。对于鼠标点击事件,QWidget 可能会触发一个信号(例如 QPushButton 的 clicked()),或改变其内部状态。

1.5、 事件的冒泡

 在 Qt 中,如果事件在当前的 QWidget 处未被完全处理,它可以继继传递给父组件。这个所谓的“事件冒泡”接着会一直持续,直到找到一个可以处理该事件的 widget 或者传递到顶层窗口。

1.6、最终处理

事件最终会被某个组件处理完成或者传递至顶层 QWidget 如果这些都不处理,事件就会被忽略,结束其生命周期。

2、源码分析

这个过程有效地确保了 Qt 应用程序可以灵活地响应各类输入事件,并允许开发者通过各种机制(如事件过滤器、重写事件处理函数等)自定义响应这些事件的行为。下面为qt处理事件的伪代码分析(windows平台为例)

// Section 1: 主函数
int main(int argc, char *argv[])
{QApplication app(argc, argv);Widget window;   // Widget继承自QWidgetwindow.show();// 进入QApplication事件循环return app.exec();
}// Section 2: QApplication事件循环
int QApplication::exec()
{// 将事件循环的处理委托给QCoreApplicationreturn QCoreApplication::exec();
}// Section 3: QCoreApplication事件循环处理
int QCoreApplication::exec()
{QThreadData *threadData = self->d_func()->threadFunc();// 确保当前方法是在主线程中调用if (threadData != QThreadData::current()) {qWarning("%s::exec: Must be called from the main thread", self->metaObject()->className());return -1;}// 检查是否已有事件循环if (!threadData->eventLoops.isEmpty()) {qWarning("QCoreApplication::exec: The event loop is already running");return -1;}QEventLoop eventLoop;self->d_func()->in_exec = true;self->d_func()->aboutToQuitEmitted = false;// 调用QEventLoop处理事件队列int returnCode = eventLoop.exec();return returnCode;
}// Section 4: QEventLoop事件循环执行
int QEventLoop::exec(ProcessEventsFlags flags)
{Q_D(QEventLoop);try {// 循环处理事件直到遇见退出指令while (!d->exit)processEvents(flags | WaitForMoreEvents | EventLoopExec);} catch (...) {}
}// Section 5: 事件处理
bool QEventLoop::processEvents(ProcessEventsFlags flags)
{Q_D(QEventLoop);if (!d->threadData->eventDispatcher)return false;if (flags & DeferredDeletion)QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);// 将事件派发给平台相关的事件分发器return d->threadData->eventDispatcher->processEvents(flags);
}// Section 6: 平台相关的事件处理,这里以Windows为例
bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
{Q_D(QEventDispatcherWin32);if (!d->internalHwnd)createInternalHwnd();d->interrupt = false;bool retVal = false;MSG msg;while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {if (!filterEvent(&msg)) {TranslateMessage(&msg);DispatchMessage(&msg);  // 分发消息给窗口程序处理}}return retVal;
}// Section 7: Windows窗口程序的消息回调函数
LRESULT CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)   
{  MSG msg = {hwnd, message, wParam, lParam};QWidget *widget = nullptr;QETWidget *qetwidget = nullptr;// 检查message是否属于Qt可转义的鼠标事件if (qt_is_translatable_mouse_event(message)) {  if (QApplication::activePopupWidget() != nullptr) {              POINT curPos = msg.pt;  // 取得鼠标点击坐标所在的QWidget指针,它指向我们在main创建的widget实例QWidget* w = QApplication::widgetAt(curPos.x, curPos.y);  if (w)  qetwidget = dynamic_cast<QETWidget*>(w);  }  // 如果鼠标事件没有被“压制处理”if (!qt_tabletChokeMouse) {  // Windows的回调函数将鼠标事件分发回给了Qt Widgetbool result = qetwidget->translateMouseEvent(msg);}}return DefWindowProc(hwnd, message, wParam, lParam);
}// Section 8: 平台依赖的鼠标事件转化
bool QETWidget::translateMouseEvent(const MSG &msg)  
{  // 这里省略了具体的鼠标事件解析代码QPoint point = QPoint(msg.lParam); // 假定lParam中存储了鼠标坐标QMouseEvent event(QEvent::MouseButtonPress, point, Qt::LeftButton, Qt::NoButton, Qt::NoModifier);  // 示例化一个鼠标事件// 将事件派发至QWidgetQApplicationPrivate::sendMouseEvent(this, &event, nullptr, this, &qt_button_down, qt_last_mouse_receiver);return true;
}// Section 9: 发送鼠标事件
bool QApplicationPrivate::sendMouseEvent(QWidget *receiver, QMouseEvent *event, QWidget *alienWidget,QWidget *nativeWidget, QWidget **buttonDown, QPointer<QWidget> &lastMouseReceiver)
{if (event->type() == QEvent::MouseButtonPress) {// 判断事件是否为自发的bool spontaneous = QCoreApplication::testAttribute(Qt::AA_SynthesizedMouseEvents);if (spontaneous)return QApplication::sendSpontaneousEvent(receiver, event);elsereturn QApplication::sendEvent(receiver, event);}return false;
}// Section 10: 实际派发事件
bool QApplication::notify(QObject *receiver, QEvent *event)
{switch (event->type()) {case QEvent::MouseButtonPress:case QEvent::MouseButtonRelease:case QEvent::MouseButtonDblClick:case QEvent::MouseMove:return d->notify_helper(receiver, event);break;default:return QCoreApplication::notify(receiver, event);}
}// Section 11: 实际处理事件的帮助函数
bool QApplicationPrivate::notify_helper(QObject *receiver, QEvent *e)
{//这里可以看到先调用事件过滤器,如果在EventerFilter拦截事件,那么事件就不会继续传递 if (sendThroughObjectEventFilters(receiver, e))return true;bool consumed = receiver->event(e);  // 事件最终送达receiver的event()方法e->setSpontaneous(false);return consumed;
}// Section 12: QWidget事件处理实现
bool QWidget::event(QEvent *event)
{switch (event->type()) {case QEvent::MouseButtonPress:mousePressEvent(static_cast<QMouseEvent*>(event));break; // 进一步处理鼠标按下事件}return true;
}

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

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

相关文章

商城积分系统的设计方案(中)-- 数模设计

一、总体设计 积分和积分渠道&#xff0c;实现积分种类和发放的动态配置&#xff0c;是设计中的关键之处。 积分订单表是不必要的&#xff0c;视具体业务需求而定。 积分账户和账户收支是核心的两个表。 后面三个表都有一个school_id&#xff0c; 其实就是租户编号&#xff…

PointMamba: A Simple State Space Model for Point Cloud Analysis

1. 论文基本信息 2. 创新点 介绍了第一个状态空间模型 PointMamba&#xff0c;将其应用与点云分析。PointMamba 表现出令人印象深刻的能力&#xff0c;包括结构简单性&#xff08;例如&#xff0c;vanilla Mamba&#xff09;、低计算成本和知识可迁移性&#xff08;例如&#…

如何将编译过的C++库迅速部署在Visual Studio新项目中

本文介绍在Visual Studio中&#xff0c;通过属性表&#xff0c;使得一个新建解决方案中的项目可以快速配置已有解决方案的项目中各类已编译好的C第三方库的方法。 例如&#xff0c;我们现有一个解决方案&#xff0c;其中的一个项目需要调用Armadillo、OpenCV等多个不同的C第三…

爆款短视频素材库有哪些?分享几个容易火的视频素材网站

当今自媒体时代&#xff0c;每位内容创作者都渴望制作出下一个爆款短视频。你是否在寻找那些能让你的视频迅速蹭热度的顶级素材库&#xff1f;本文将为你介绍几个视频素材库&#xff0c;它们或许能成为你成功的秘密武器。首先要提的&#xff0c;自然是著名的国内素材库——蛙学…

fetch-jsonp源码阅读

fetchJsonp源码 源码地址&#xff1a;https://github.com/camsong/fetch-jsonp jsonp优势 请求数据没有跨域的限制,后台不用考虑跨域问题 对于老版本浏览器更加支持 jsonp缺陷 只支持get请求,不支持其他所有方式的请求(请求方式受到了限制)只支持get请求,不支持post(不安…

SAP PP学习笔记24 - 生产订单(制造指图)的创建

上面两章讲了生产订单的元素。 SAP PP学习笔记22 - 生产订单&#xff08;制造指图&#xff09;的元素1-CSDN博客 SAP PP学习笔记23 - 生产订单&#xff08;制造指图&#xff09;的元素2 - 决济规则(结算规则)-CSDN博客 这一章讲生产订单的创建。比如 - 生产订单的流程&#…

【课程总结】Day12:YOLO的深入了解

前言 在【课程总结】Day11&#xff08;下&#xff09;&#xff1a;YOLO的入门使用一节中&#xff0c;我们已经了解YOLO的使用方法&#xff0c;使用过程非常简单&#xff0c;训练时只需要三行代码&#xff1a;引入YOLO&#xff0c;构建模型&#xff0c;训练模型&#xff1b;预测…

SwiftUI八与UIKIT交互

代码下载 SwiftUI可以在苹果全平台上无缝兼容现有的UI框架。例如&#xff0c;可以在SwiftUI视图中嵌入UIKit视图或UIKit视图控制器&#xff0c;反过来在UIKit视图或UIKit视图控制器中也可以嵌入SwiftUI视图。 本文展示如何把landmark应用的主页混合使用UIPageViewController和…

代码随想录算法训练营第五十二天 | 647. 回文子串、516.最长回文子序列、动态规划总结篇、复习

647. 回文子串 题目链接&#xff1a;https://leetcode.cn/problems/palindromic-substrings/ 文档讲解&#xff1a;https://programmercarl.com/0647.%E5%9B%9E%E6%96%87%E5%AD%90%E4%B8%B2.html 视频讲解&#xff1a;https://www.bilibili.com/video/BV17G4y1y7z9/ 思路 确定…

Meta CEO 扎克伯格批评闭源AI竞争对手:称其试图“创造上帝”|TodayAI

美国社交媒体巨头Meta(Facebook母公司)的CEO马克扎克伯格&#xff08;Mark Zuckerberg&#xff09;近日在一次采访中&#xff0c;公开批评了那些他认为不够开放的AI竞争对手&#xff0c;称他们的行为就像是在“创造上帝”。扎克伯格坚定表示&#xff0c;AI技术不应该被某一家公…

git如何切换到tag分支

项目场景&#xff1a; 当我们需要回退到某个tag分支。 问题描述 通过git命令 git checkout tag_name 执行这个命令后&#xff0c;会提示你当前处于一个“detached HEAD”的状态。 原因分析&#xff1a; 这是因为tag只是一个快照&#xff0c;是不能更改代码的。 解决方案&am…

SysML与MBSE的关系

SysML与MBSE的关系 对于任何基于模型的系统工程 &#xff08;MBSE&#xff09; 方法&#xff0c;推荐的最佳实践是基于模型的语言、基于模型的工具、基于模型的流程和基于模型的架构框架的协同应用&#xff0c;如下图所示 系统架构四元组 图。经过十年将SysML应用于棘手的系统…

2535. 数组元素和与数字和的绝对差

给你一个正整数数组 nums 。 元素和 是 nums 中的所有元素相加求和。数字和 是 nums 中每一个元素的每一数位&#xff08;重复数位需多次求和&#xff09;相加求和。 返回 元素和 与 数字和 的绝对差。 注意&#xff1a;两个整数 x 和 y 的绝对差定义为 |x - y| 。 示例 1&a…

面试真题及答题思路(二)

题目 某地为了开发旅游资源&#xff0c;花费 600万元建了一个涉嫌抄袭的牛郎织女雕塑&#xff0c;变成了网红打卡地&#xff0c;也吸引了游客&#xff0c;但是部分网民说太丑&#xff0c;花的钱太多。对此&#xff0c;你怎么看&#xff1f;为了缓解辖区内双职工家庭的育儿压力…

戴尔md3400存储控制器脱机故障 电池故障处理

看了一下网上关于DELL MD系列存储故障处理的文档还是比较少的&#xff0c;最近处理了一些关于MD系列存储的问题&#xff0c;稍微整理整理就分享一下&#xff0c;各位喜欢摸索的朋友可以稍稍做些参考&#xff0c;当然如果想寻求外援的也可以快速的找到合适的人。以便安全又快捷的…

C语言基础——操作符

ʕ • ᴥ • ʔ づ♡ど &#x1f389; 欢迎点赞支持&#x1f389; 个人主页&#xff1a;励志不掉头发的内向程序员&#xff1b; 专栏主页&#xff1a;C语言基础&#xff1b; 文章目录 前言 一、操作符的分类 二、二进制和进制转换 2.1 二进制转十进制 2.1.1 十…

零知识证明基础:数字签名

1、绪论 数字签名(Digital Signature)&#xff0c;也称电子签名&#xff0c;是指附加在某一电子文档中的一组特定的符号或代码。它利用密码技术对该电子文档进行关信息提取并进行认证形成&#xff0c;用于标识签发者的身份以及签发者对电子文档的认可&#xff0c;并能被接收者…

pyqt5 制作视频剪辑软件,切割视频

该软件用于切割视频,手动选取视频片段的起始帧和结束帧并保存为json文件。gui界面如下:包含快进、快退、暂停等功能, 代码如下: # coding=UTF-8 """ theme: pyqt5实现动作起始帧和结束帧的定位,将定位到的帧数保存json文件 time: 2024-6-27 author: cong…

详细介绍LP-SCADA系统的核心数据采集单元

关键字:LP-SCADA系统, 传感器可视化, 设备可视化, 独立SPC系统, 智能仪表系统,SPC可视化,独立SPC系统 SCADA系统的数据采集功能是其核心组成部分&#xff0c;它允许系统从各种传感器、仪器和设备中收集实时数据。以下是SCADA系统数据采集功能的详细描述&#xff1a; 传感器和…

Kotlin vs Java:深入解析两者之间的最新差异与优劣(全面指南)

文章目录 1. 概述2. 语法简洁性3. 空安全4. 扩展函数5. 协程6. 数据类7. 智能类型转换8. 默认参数与命名参数9. 无 checked exceptions10. 单例模式总结 &#x1f389;欢迎来到Java学习路线专栏~探索Java中的静态变量与实例变量 ☆* o(≧▽≦)o *☆嗨~我是IT陈寒&#x1f379;✨…