当前位置: 首页 > news >正文

Qt 中最经典、最常用的多线程通信场景

实现步骤

  1. 创建工作类 (Worker):在工作线程中处理数据的对象。

  2. 创建线程对象 (QThread):用来托管工作对象。

  3. 连接信号槽

    • 主线程 -> 工作线程:连接一个主窗口发出的信号工作对象的槽,用于传递数据。

    • 工作线程 -> 主线程:连接工作对象发出的信号主窗口的槽,用于返回结果、更新UI。

  4. 移动工作对象:将工作对象移动到新线程。

  5. 启动线程:启动线程的事件循环。

  6. 按钮点击:在按钮点击的槽函数中,发射那个用于传递数据的信号。

完整代码示例

1. 工作类 (worker.h)

// worker.h
#ifndef WORKER_H
#define WORKER_H#include <QObject>
#include <QString>
#include <QDebug>
#include <QThread>class Worker : public QObject
{Q_OBJECTpublic:explicit Worker(QObject *parent = nullptr) : QObject(parent) {}public slots:// 这个槽函数专门用来接收主线程发来的数据并进行处理void handleDataFromMainThread(const QString &data) {qDebug() << "工作线程ID:" << QThread::currentThreadId();qDebug() << "收到主线程发来的数据:" << data;// 模拟一个耗时的数据处理过程QString result;for (int i = 0; i < data.length(); ++i) {QThread::msleep(200); // 模拟处理每个字符需要时间result.prepend(data[i]); // 做一个简单的反转操作作为处理示例emit progress(i+1, data.length()); // 发送处理进度}// 处理完成,发射信号将结果发回主线程emit dataProcessed(result);}signals:// 处理进度信号 (当前进度, 总数)void progress(int current, int total);// 处理完成信号 (结果)void dataProcessed(const QString &result);
};#endif // WORKER_H

2. 主窗口头文件 (mainwindow.h)

// mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();// 新增一个信号,用于向工作线程发送数据
signals:void sendDataToWorker(const QString &data);private slots:// 按钮点击的槽函数void on_pushButtonStart_clicked();// 接收工作线程发回的信号的槽函数void updateProgress(int current, int total);void handleResult(const QString &result);private:Ui::MainWindow *ui;QThread *workerThread;
};#endif // MAINWINDOW_H

3. 主窗口实现 (mainwindow.cpp) - 核心

// mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "worker.h" // 包含工作类头文件
#include <QThread>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow), workerThread(new QThread(this)) // 线程对象的父对象是主窗口,生命周期由其管理
{ui->setupUi(this);// 创建工作者对象,此时它还在主线程Worker *worker = new Worker;// !!! 关键步骤:将工作者对象移动到新线程 !!!worker->moveToThread(workerThread);// !!! 核心连接:主线程 -> 工作线程 !!!// 当主窗口发射 sendDataToWorker 信号时, worker 的 handleDataFromMainThread 槽函数会被调用// 因为 worker 已移动到新线程,这个连接会自动使用 QueuedConnection,保证线程安全connect(this, &MainWindow::sendDataToWorker, worker, &Worker::handleDataFromMainThread);// 连接:工作线程 -> 主线程 (用于更新UI)connect(worker, &Worker::progress, this, &MainWindow::updateProgress);connect(worker, &Worker::dataProcessed, this, &MainWindow::handleResult);// 连接线程结束信号,用于自动清理对象connect(workerThread, &QThread::finished, worker, &QObject::deleteLater);connect(workerThread, &QThread::finished, workerThread, &QObject::deleteLater);// 启动线程(启动事件循环)workerThread->start();// 初始化UIui->progressBar->setValue(0);
}MainWindow::~MainWindow()
{// 优雅退出线程if (workerThread && workerThread->isRunning()) {workerThread->quit();workerThread->wait();}delete ui;
}// 按钮点击事件
void MainWindow::on_pushButtonStart_clicked()
{QString inputData = ui->lineEditInput->text();if (inputData.isEmpty()) {return;}// 禁用按钮,防止重复点击ui->pushButtonStart->setEnabled(false);ui->progressBar->setValue(0);// !!! 核心操作:发射信号,将数据发送到工作线程 !!!qDebug() << "主线程ID:" << QThread::currentThreadId() << ",发射信号,数据:" << inputData;emit sendDataToWorker(inputData);
}// 更新进度条
void MainWindow::updateProgress(int current, int total)
{int percent = (current * 100) / total;ui->progressBar->setValue(percent);ui->labelStatus->setText(QString("处理中: %1/%2").arg(current).arg(total));
}// 显示处理结果
void MainWindow::handleResult(const QString &result)
{ui->textEditResult->setText(result);ui->labelStatus->setText("处理完成!");ui->pushButtonStart->setEnabled(true); // 重新启用按钮
}

http://www.dtcms.com/a/341216.html

相关文章:

  • TDengine IDMP 运维指南(数据导入导出)
  • WIN10/WIN11:无法下载所有必需的文件 0x80072EE2 0x20000(未解决)
  • C++ std::sort的应用总结
  • Unity 大量子弹和小怪碰撞检测优化
  • GSPO:Towards scalable reinforcement learning for language models
  • Baumer工业相机堡盟工业相机如何通过YoloV8深度学习模型和EasyOCR实现汽车牌照动态检测和识别(C#代码,UI界面版)
  • 使用UUP dump制作windows preview镜像
  • 手机、汽车如何实现卫星直连
  • imx6ull-驱动开发篇31——Linux异步通知
  • 玩转QEMU硬件模拟器 - Raspberry Pi OS驱动开发
  • 【项目复盘】【四轴飞行器设计】驱动开发部分
  • Redis 安装教程
  • 【数据结构之二叉树】
  • 【openssl】openssl CA.pl 签发证书操作步骤
  • redis执行lua脚本的原子性和数据库原子性的区别
  • [激光原理与应用-315]:光学设计 - SolidWorks, 光机系统设计的神器,打通光学与机械设计的闭环
  • Tomcat部署与HTTP协议详解
  • 佳维视工业一体机在公共交通系统配套中的应用
  • 疯狂星期四文案网第45天运营日记
  • LTspice仿真电路:(三十五)LED恒流驱动仿真(LT3497)
  • burpsuite+captcha-killer插件识别图片验证码进行爆破
  • AiPy 文档自动化处理实践:从 docx 到结构化 db 的高效转换方案
  • 华为仓颉语言的class(类)初步
  • ES Modules +案例分析
  • 【C++】动态导入Windows系统API的简单方法
  • Docker复杂安装--最详细的MySQL主从复制与Redis集群安装、主从复制、主从扩容主从缩容实战版
  • 03-dockerfile
  • 8月7日国赛全真模拟!2025“华数杯”数学建模竞赛,常用模型及算法总结
  • 网络连接的核心机制
  • Python 数据可视化:Matplotlib 与 Seaborn 实战