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

news/2024/12/22 22:22:51/

 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.ppmy.cn/news/1472581.html

相关文章

centos 7.9 离线环境安装GPU服务环境

文章目录 centos 7.9 离线环境安装GPU服务环境系统配置更新 gcc更新内核安装显卡驱动安装cuda安装docker 和 nvidia-container-runtime验证 centos 7.9 离线环境安装GPU服务环境 基于centos 7.9 离线安装gpu 服务基础环境&#xff0c;用于在docker 中运行算法服务 系统配置 …

【华为OD机试B卷】解压报文、压缩报文还原(C++/Java/Python)

题目 题目描述 为了提升数据传输的效率,会对传输的报文进行压缩处理。输入一个压缩后的报文,请返回它解压后的原始报文。压缩规则:n[str],表示方括号内部的 str 正好重复 n 次。注意 n 为正整数(0 < n <= 100),str只包含小写英文字母,不考虑异常情况。输入描述 输…

ESP32-C3(基本信息)

ESP32-C3 是一款低功耗、高集成度的 MCU 系统级芯片 (SoC)&#xff0c;它集成了 2.4 GHz Wi-Fi 和低功耗蓝牙 (Bluetooth LE) 无线通信功能&#xff0c;并拥有丰富的外设接口和先进的电源管理机制。 主要特性&#xff1a; 无线通信&#xff1a; 支持 2.4 GHz Wi-Fi (802.11b/…

【Linux】虚拟机安装openEuler 24.03 X86_64 教程

目录 一、概述 1.1 openEuler 覆盖全场景的创新平台 1.2 系统框架 1.3 平台框架 二、安装详细步骤 一、概述 1.1 openEuler 覆盖全场景的创新平台 openEuler 已支持 x86、Arm、SW64、RISC-V、LoongArch 多处理器架构&#xff0c;逐步扩展 PowerPC 等更多芯片架构支持&…

[leetcode]avoid-flood-in-the-city 避免洪水泛滥

. - 力扣&#xff08;LeetCode&#xff09; class Solution { public:vector<int> avoidFlood(vector<int>& rains) {vector<int> ans(rains.size(), 1);set<int> st;unordered_map<int, int> mp;for (int i 0; i < rains.size(); i) {i…

【PyTorch单点知识】神经元网络模型剪枝prune模块介绍(下,结构化剪枝)

文章目录 0. 前言1. 结构化剪枝 vs 非结构化剪枝1.1 非结构化剪枝的特征1.2 结构化剪枝1.3 结构化剪枝的好处&#xff1a; 2. torch.nn.utils.prune中的结构化剪枝方法3. PyTorch实例3.1 random_structured3.2 prune.ln_structured 4. 总结 0. 前言 按照国际惯例&#xff0c;首…

Flutter【组件】点击类型表单项

简介 flutter 点击表单项组件&#xff0c;适合用户输入表单的场景。 点击表单项组件是一个用户界面元素&#xff0c;通常用于表单或设置界面中&#xff0c;以便用户可以点击它们来选择或更改某些设置或输入内容。这类组件通常由一个标签和一个可点击区域组成&#xff0c;并且…

【AI原理解析】—支持向量机原理

目录 1. 支持向量机&#xff08;SVM&#xff09;概述 2. 超平面与支持向量 3. 间隔最大化 4. 优化问题 5. 核函数 6. 总结 1. 支持向量机&#xff08;SVM&#xff09;概述 定义&#xff1a;支持向量机是一种监督学习模型&#xff0c;主要用于数据分类问题。其基本思想是…