QT开发:深入详解QtCore模块事件处理,一文学懂QT 事件循环与处理机制

d49682468ebb43a5b217044a46d7fbee.jpg

 

Qt 是一个跨平台的 C++ 应用程序框架,QtCore 模块提供了核心的非 GUI 功能。事件处理是 Qt 应用程序的重要组成部分。Qt 的事件处理机制包括事件循环和事件处理,它们共同确保应用程序能够响应用户输入、定时器事件和其他事件。

1. 事件循环(Event Loop)

Qt 的事件循环是一个无限循环,它从操作系统获取事件并分发给应用程序中的合适对象。事件循环由 QCoreApplication 或其子类(如 QApplication)管理。

2. 事件对象(Event Object)

Qt 使用 QEvent 类及其子类来封装事件信息。例如,QMouseEvent 用于鼠标事件,QKeyEvent 用于键盘事件。每个事件类型都有一个唯一的类型标识符。

3. 事件处理(Event Handling)

事件处理包括两个核心部分:事件过滤(Event Filtering)和事件处理(Event Handling)。Qt 提供了几个机制来处理事件:

  • 事件过滤器(Event Filters):可以在事件到达目标对象之前拦截事件。
  • 事件处理器(Event Handlers):对象可以通过重写特定的事件处理方法来处理事件。

4. 事件循环的实现

以下是事件循环的基本实现:

#include <QCoreApplication>
#include <QTimer>
#include <QDebug>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    // 创建一个定时器,定时发出超时信号并退出应用程序
    QTimer::singleShot(5000, &a, &QCoreApplication::quit);

    qDebug() << "Event loop starting.";

    // 进入事件循环
    int ret = a.exec();

    qDebug() << "Event loop exited.";

    return ret;
}

5. 事件处理机制

下面是一个详细的事件处理示例,包含自定义事件、事件过滤器和事件处理器。

自定义事件

首先,我们定义一个自定义事件:

#include <QEvent>
#include <QString>

// 自定义事件类,继承自 QEvent
class MyCustomEvent : public QEvent {
public:
    // 定义一个唯一的事件类型
    static const QEvent::Type MyEventType = static_cast<QEvent::Type>(QEvent::User + 1);

    // 构造函数,接受一个消息字符串
    MyCustomEvent(const QString &message)
        : QEvent(MyEventType), message(message) {}

    // 获取消息
    QString getMessage() const { return message; }

private:
    QString message;  // 事件携带的消息
};

自定义对象

接下来,创建一个自定义对象,重写 event() 函数来处理自定义事件:

#include <QObject>
#include <QDebug>

// 自定义对象类,继承自 QObject
class MyObject : public QObject {
    Q_OBJECT

protected:
    // 重写 event() 方法,处理自定义事件
    bool event(QEvent *event) override {
        if (event->type() == MyCustomEvent::MyEventType) {
            MyCustomEvent *myEvent = static_cast<MyCustomEvent*>(event);
            qDebug() << "Custom event received with message:" << myEvent->getMessage();
            return true;  // 事件已处理
        }
        return QObject::event(event);  // 传递给父类处理
    }
};

事件过滤器

创建一个事件过滤器类:

#include <QObject>
#include <QEvent>
#include <QDebug>

// 自定义事件过滤器类,继承自 QObject
class MyEventFilter : public QObject {
    Q_OBJECT

protected:
    // 重写 eventFilter() 方法,过滤自定义事件
    bool eventFilter(QObject *obj, QEvent *event) override {
        if (event->type() == MyCustomEvent::MyEventType) {
            MyCustomEvent *myEvent = static_cast<MyCustomEvent*>(event);
            qDebug() << "Event filter caught custom event with message:" << myEvent->getMessage();
            return true;  // 阻止事件进一步传播
        }
        return QObject::eventFilter(obj, event);  // 传递给父类处理
    }
};

主程序

最后,在主程序中使用这些类:

#include <QCoreApplication>
#include <QTimer>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    MyObject obj;
    MyEventFilter filter;

    // 安装事件过滤器
    obj.installEventFilter(&filter);

    // 创建并发送自定义事件
    MyCustomEvent *event = new MyCustomEvent("Hello, Qt!");
    QCoreApplication::postEvent(&obj, event);

    // 创建一个定时器,定时退出应用程序
    QTimer::singleShot(5000, &a, &QCoreApplication::quit);

    return a.exec();  // 进入事件循环
}

注释与总结

  • QCoreApplication:管理事件循环。
  • QEvent:所有事件的基类,自定义事件继承自 QEvent
  • event():重写此方法以处理特定事件。
  • eventFilter():重写此方法以在事件到达目标对象之前拦截事件。
  • postEvent():将事件放入事件队列中。
  • singleShot():创建一个单次定时器,用于触发事件或动作。

通过以上示例,我们详细展示了 Qt 中事件循环和事件处理的基本机制。

事件循环的应用场景

Qt 事件循环在各种应用场景中都有广泛应用,包括 GUI 应用程序、定时任务、异步操作、并发处理、自定义事件、数据库操作和文件 I/O 等。以下是一些具体的应用场景和对应的示例代码:

1. GUI 应用程序

在 GUI 应用程序中,事件循环用于捕获和处理用户交互事件,如鼠标点击、键盘输入、窗口移动和调整大小等。

#include <QApplication>
#include <QPushButton>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    // 创建一个按钮
    QPushButton button("Hello, Qt!");
    button.show();

    // 进入事件循环
    return app.exec();
}

 

2. 定时任务

Qt 提供了定时器功能,通过 QTimer 类可以设置定时任务。事件循环会捕获定时器的超时事件,并调用预设的槽函数。

#include <QCoreApplication>
#include <QTimer>
#include <QDebug>

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);

    QTimer timer;
    QObject::connect(&timer, &QTimer::timeout, [](){
        qDebug() << "Timer timeout!";
    });
    timer.start(1000);  // 每隔一秒触发一次

    return app.exec();  // 进入事件循环
}

3. 异步操作

Qt 的 QNetworkAccessManager 提供了对网络请求的支持。通过事件循环处理网络请求的响应,避免了阻塞主线程。

#include <QCoreApplication>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QUrl>
#include <QDebug>

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);

    QNetworkAccessManager manager;
    QNetworkReply *reply = manager.get(QNetworkRequest(QUrl("https://www.qt.io")));
    
    QObject::connect(reply, &QNetworkReply::finished, [=]() {
        qDebug() << "Network request finished";
        qDebug() << reply->readAll();
        reply->deleteLater();
        app.quit();
    });

    return app.exec();  // 进入事件循环
}

4. 并发处理

Qt 提供了多线程支持,通过事件循环可以实现线程间的通信。例如,主线程和工作线程之间的信号和槽连接。

#include <QCoreApplication>
#include <QThread>
#include <QDebug>

class Worker : public QObject {
    Q_OBJECT
public slots:
    void doWork() {
        qDebug() << "Work done in thread:" << QThread::currentThread();
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);

    QThread workerThread;
    Worker worker;
    worker.moveToThread(&workerThread);

    QObject::connect(&workerThread, &QThread::started, &worker, &Worker::doWork);
    QObject::connect(&worker, &Worker::doWork, &app, &QCoreApplication::quit);

    workerThread.start();

    return app.exec();  // 进入事件循环
}

#include "main.moc"

5. 自定义事件

除了 Qt 提供的标准事件类型,用户也可以定义自己的事件类型,并在事件循环中处理它们。

#include <QCoreApplication>
#include <QEvent>
#include <QDebug>

class MyCustomEvent : public QEvent {
public:
    static const QEvent::Type MyEventType = static_cast<QEvent::Type>(QEvent::User + 1);
    MyCustomEvent(const QString &message) : QEvent(MyEventType), message(message) {}
    QString getMessage() const { return message; }

private:
    QString message;
};

class MyObject : public QObject {
    Q_OBJECT
protected:
    bool event(QEvent *event) override {
        if (event->type() == MyCustomEvent::MyEventType) {
            MyCustomEvent *myEvent = static_cast<MyCustomEvent*>(event);
            qDebug() << "Custom event received with message:" << myEvent->getMessage();
            return true;
        }
        return QObject::event(event);
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);

    MyObject obj;
    MyCustomEvent *event = new MyCustomEvent("Hello, Custom Event!");
    QCoreApplication::postEvent(&obj, event);

    QTimer::singleShot(2000, &app, &QCoreApplication::quit);  // 定时退出应用程序

    return app.exec();  // 进入事件循环
}

#include "main.moc"

6. 数据库操作

通过事件循环,可以在不阻塞用户界面的情况下执行数据库查询。需要将查询操作放到另一个线程中执行。

#include <QCoreApplication>
#include <QtSql>
#include <QDebug>
#include <QThread>

class DbWorker : public QObject {
    Q_OBJECT
public:
    void runQuery() {
        QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
        db.setDatabaseName(":memory:");
        if (!db.open()) {
            emit error("Failed to open database");
            return;
        }

        QSqlQuery query;
        query.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)");
        query.exec("INSERT INTO test (value) VALUES ('Hello, Database')");

        QSqlQuery asyncQuery(db);
        asyncQuery.exec("SELECT value FROM test WHERE id=1");

        if (asyncQuery.next()) {
            emit resultReady(asyncQuery.value(0).toString());
        } else {
            emit error("Query failed");
        }
    }

signals:
    void resultReady(const QString &result);
    void error(const QString &errMsg);
};

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);

    QThread workerThread;
    DbWorker worker;

    worker.moveToThread(&workerThread);

    QObject::connect(&workerThread, &QThread::started, &worker, &DbWorker::runQuery);
    QObject::connect(&worker, &DbWorker::resultReady, [&](const QString &result) {
        qDebug() << "Query result:" << result;
        workerThread.quit();
    });
    QObject::connect(&worker, &DbWorker::error, [&](const QString &errMsg) {
        qDebug() << errMsg;
        workerThread.quit();
    });
    QObject::connect(&workerThread, &QThread::finished, &app, &QCoreApplication::quit);

    workerThread.start();

    return app.exec();  // 进入事件循环
}

#include "main.moc"

7. 文件 I/O 操作

通过事件循环,可以在不阻塞用户界面的情况下读取或写入文件。需要将文件读取操作放到另一个线程中执行。

#include <QCoreApplication>
#include <QFile>
#include <QTextStream>
#include <QDebug>
#include <QThread>

class FileWorker : public QObject {
    Q_OBJECT
public:
    FileWorker(const QString &filePath) : filePath(filePath) {}

public slots:
    void doWork() {
        QFile file(filePath);
        if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
            emit error("Failed to open file");
            return;
        }

        QTextStream in(&file);
        QString content = in.readAll();
        file.close();

        emit fileRead(content);
    }

signals:
    void fileRead(const QString &content);
    void error(const QString &errMsg);

private:
    QString filePath;
};

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);

    QThread workerThread;
    FileWorker worker("test.txt");

    worker.moveToThread(&workerThread);

    QObject::connect(&workerThread, &QThread::started, &worker, &FileWorker::doWork);
    QObject::connect(&worker, &FileWorker::fileRead, [&](const QString &content) {
        qDebug() << "File content:" << content;
        workerThread.quit();  // 文件读取完成后退出线程
    });
    QObject::connect(&worker, &FileWorker::error, [&](const QString &errMsg) {
        qDebug() << errMsg;
        workerThread.quit();  // 发生错误时退出线程
    });
    QObject::connect(&workerThread, &QThread::finished, &app, &QCoreApplication::quit);

    workerThread.start();

    return app.exec();  // 进入事件循环
}

#include "main.moc"

总结

Qt 事件循环广泛应用于各种场景,如 GUI 应用程序的用户交互、定时任务、网络通信、并发处理、自定义事件、异步数据库查询和异步文件 I/O 等。通过合理利用事件循环,可以确保应用程序在处理各种事件时高效、顺畅地运行。希望这些示例能帮助你更好地理解 QtCore 模块中的事件处理机制及其应用场景。

 

 

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

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

相关文章

LabVIEW机动车动态制动性能校准系统

机动车动态制动性能测试系统通过高精度的硬件设备与LabVIEW软件的紧密配合&#xff0c;实现了对机动车制动性能的精确校准与评估。系统不仅提高了测试的精确性和效率&#xff0c;而且具备良好的用户交互界面&#xff0c;使得操作更加简便、直观。 项目背景 随着机动车辆数量的…

【数据结构】十大经典排序算法总结与分析

文章目录 前言1. 十大经典排序算法分类2. 相关概念3. 十大经典算法总结4. 补充内容4.1 比较排序和非比较排序的区别4.2 稳定的算法就真的稳定了吗&#xff1f;4.3 稳定的意义4.4 时间复杂度的补充4.5 空间复杂度补充 结语 前言 排序算法是《数据结构与算法》中最基本的算法之一…

波克城市 x NebulaGraph|高效数据血缘系统在游戏领域的构建实战

关于波克城市和作者‍‍ 波克城市&#xff0c;一家专注于研发精品休闲游戏的全球化公司&#xff0c;连续七年入选中国互联网综合实力百强&#xff0c;2023 年位列 17 位。波克城市旗下拥有《捕鱼达人》《猫咪公寓2》等精品休闲游戏&#xff0c;全球注册用户超 5 亿&#xff0c;…

量化交易backtrader实践(一)_数据获取篇(3)_爬取数据

这一节实践其实是在上一节之前进行的&#xff0c;背景原因是因为tushare.pro的积分不够高&#xff0c;当时还没有接触到使用akshare等其他接口&#xff0c;因此对于全股票列表用的是去网页上爬的方式获得的&#xff0c;也就借此机会&#xff0c;再复习了一遍爬虫的相关知识。 …

【Linux】从内核认识信号

一、阻塞信号 1 .信号的一些其他相关概念 实际执行信号的处理动作称为信号递达(Delivery) 信号从产生到递达之间的状态,称为信号未决(Pending)。 进程可以选择阻塞 (Block )某个信号。 被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作. 注…

Element走马灯组件循环播放两个页面是方向不一致

摘要&#xff1a;使用Carousel 走马灯循环播放同一类型的图片、文字等内容&#xff0c;会在循环内容为两组是出现下图 [1]中的现象。本文记录下如何解决 之前项目遇到过一次这个问题&#xff0c;由于indicator-position 指示器不用显示&#xff0c;则判断内容长度为2时&#xf…

SpringBoot2:web开发常用功能实现及原理解析-上传与下载

文章目录 一、上传文件1、前端上传文件给Java接口2、Java接口上传文件给Java接口 二、下载文件1、前端调用Java接口下载文件2、Java接口下载网络文件到本地3、前端调用Java接口下载网络文件 一、上传文件 1、前端上传文件给Java接口 Controller接口 此接口支持上传单个文件和…

干货:分享6款ai论文写作助手,一键生成原创论文(步骤+工具)

写一篇论文是一个复杂的过程&#xff0c;涉及多个步骤&#xff0c;包括选题、研究、撰写、编辑和校对。AI可以在其中的一些步骤中提供帮助&#xff0c;但最终的论文还是需要人类作者的深入思考和创造性输入。以下是六款值得推荐的AI论文写作助手&#xff0c;其中特别推荐千笔-A…

IDEA 新版本设置菜单展开

IDEA 新版本设置菜单展开 使用了新版本的IDEA 新UI后&#xff0c;常用的file&#xff0c;edit&#xff0c;view&#xff0c;菜单隐藏了 在设置中搜索后show main menu in a separate toolbar中可以打开。 打开设置 搜索show main menu in a separate toolbar后勾上

keil调试变量值被篡改问题

今天遇到一个代码中变量值被篡改的问题&#xff0c;某个数组的第一个值运行一段时间之后变成了0&#xff0c;如图&#xff1a; 看现象基本可以断定是内存越界导致的&#xff0c;但是要如果定位是哪里内存越界呢? keil提供了两个工具 1、set access breakpoint at(设置访问断点…

TensorFlow深度学习框架改进K-means聚类、SOM自组织映射算法及上海招生政策影响分析研究...

全文链接&#xff1a;https://tecdat.cn/?p37652 分析师&#xff1a;Chen Zhang 在教育政策研究领域&#xff0c;准确评估政策对不同区域和学生群体的影响至关重要。2021 年上海市出台的《上海市初中学业水平考试实施办法》对招生政策进行了调整&#xff0c;其中名额分配综合…

通过C# 裁剪PDF页面

在处理PDF文档时&#xff0c;有时需要精确地裁剪页面以适应特定需求&#xff0c;比如去除广告、背景信息或者仅仅是为了简化文档内容。 本文将指导如何使用免费.NET控件通过C#实现裁剪PDF页面。 免费库 Free Spire.PDF for .NET 支持在 .NET (C#, VB.NET, ASP.NET, .NET Core)…

java数据结构----图

图的存储结构: 代码实现 public class Graph {// 标记顶点数目private int V;// 标记边数目private int E;// 邻接表private Queue<Integer>[] adj;public Graph(int v) {V v;this.E 0;this.adj new Queue[v];for (int i 0; i < adj.length; i) {adj[i] new Queu…

使用阿里OCR身份证识别

1、开通服务 免费试用 2、获取accesskay AccessKeyId和AccessKeySecret 要同时复制保存下来 因为后面好像看不AccessKeySecret了 3.Api 参考 https://help.aliyun.com/zh/ocr/developer-reference/api-ocr-api-2021-07-07-recognizeidcard?spma2c4g.11186623.0.0.7a9f4b1e5C…

PHP及Java等其他语言转Go时选择GoFly快速快速开发框架指南

概要 经过一年多的发展GoFly快速开发框架已被一千多家科技企业或开发者用于项目开发&#xff0c;他的简单易学得到其他语言转Go首选框架。且企业版的发展为GoFly社区提供资金&#xff0c;这使得GoFly快速框架得到良好的发展&#xff0c;GoFly技术团队加大投入反哺科技企业和开…

红黑树的插入(NGINX源码)

下载并查看NGINX源码 访问NGINX下载页面&#xff0c;找到所需版本 https://nginx.org/en/download.html 使用wget下载源码包&#xff0c;替换版本号为所需版本 wget http://nginx.org/download/nginx-1.24.0.tar.gz解压源码包 tar -xzvf nginx-1.24.0.tar.gz进入解压后的目…

【算法题】64. 最小路径和-力扣(LeetCode)

【算法题】64. 最小路径和-力扣(LeetCode) 1.题目 下方是力扣官方题目的地址 64. 最小路径和 给定一个包含非负整数的 *m* x *n* 网格 grid &#xff0c;请找出一条从左上角到右下角的路径&#xff0c;使得路径上的数字总和为最小。 **说明&#xff1a;**每次只能向下或者…

Tiny Universe - Llama3架构

Llama3和Llama2和Qwen2的整体架构相似&#xff0c;本篇文章主要讲解它们的一些主要不同点。 关于Qwen2架构可参考 Qwen2架构 学习笔记 llama3区别于llama2在模型层面的区别主要体现在全模型使用GQA。 基础知识 MLP MLP&#xff08;Multi-Layer Perceptron&#xff09;多层感…

1 elasticsearch安装

【0】官网参考 https://www.elastic.co/guide/en/elasticsearch/reference/7.11/targz.html 【1】Centos7 下载安装 【1.1】下载 官网&#xff1a;Download Elasticsearch | Elastic 选择好自己想要的相关版本即可&#xff1b; 【2】Centos7.X 前置环境配置&#xff08;uli…

STM32MP157/linux驱动学习记录(二)

38.Linux INPUT 子系统实验 按键、鼠标、键盘、触摸屏等都属于输入(input)设备&#xff0c;Linux 内核为此专门做了一个叫做 input子系统的框架来处理输入事件。输入设备本质上还是字符设备&#xff0c;只是在此基础上套上了 input 框架&#xff0c;用户只需要负责上报输入事件…