Qt 线程

💡 进度条显示拷贝进度(verson 1)

窗口上放置一个按钮和一个进度条部件,点击按钮,进行拷贝操作 —— 打开对话框选择源文件,然后再打开一个对话框 选择 目标文件存放位置和名称。拷贝过程中进度条显示当前进度(大文件)。不允许用QFile 的 copy 方法 —— 把源文件分解开,然后一帧一帧写入目标文件。
在这里插入图片描述

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QtWidgets>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

private slots:
    void on_pushButton_released();

private:
    Ui::Widget *ui;
};

#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"

#define KB 1024

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    ui->progressBar->setValue(0);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::on_pushButton_released()
{
    // 1.选定源文件
    QString srcName = QFileDialog::getOpenFileName(this, tr("选择文件"), \
    							"D:/Packages_", tr("all  (*.*)"));
    if (srcName.isEmpty())
        return ;
    qDebug() << "src: " << srcName;

    // 2.选定目标文件
    QString destName = QFileDialog::getSaveFileName(this, tr("保存文件"), \
    							"e:", tr("all  (*.*)"));
    if (destName.isEmpty())
        return ;
    qDebug() << "dest: " << destName;

    
    QFile srcFile(srcName);
    if (!srcFile.open(QIODevice::ReadOnly))
    {
        qDebug() << "打开文件失败";
        return ;
    }

    QFile destFile(destName);
    if (!destFile.open(QIODevice::ReadWrite | QIODevice::Truncate))
    {
        qDebug() << "复制文件失败";
        return ;
    }

    
    ui->progressBar->setValue(0);			// 确保每次点击按钮时,进度条都是从 0 开始
    qint64 totalSize = srcFile.size();
    qint64 currentSize = 0;
    QByteArray buffer;

    // 3.开始拷贝--不断读取,然后写入
    while (!srcFile.atEnd())
    {
        buffer.clear();
        buffer = srcFile.read(KB);
        destFile.write(buffer);

        // 4.更新进度值
        currentSize += buffer.size();
        ui->progressBar->setValue(100 * currentSize / totalSize);
    }
}

运行结果如下:(复制文件时,拖动窗口会产生卡顿)
在这里插入图片描述

QT 线程

GUI 线程(QT 的主线程)

QT 的主线程称为 GUI 线程,负责初始化界面监听事件循环,并根据事件处理做出界面上的反馈。

使用多线程的好处

1、提高应用界面的响应速度;

这对于开发图形界面程序尤其重要,当一个操作耗时很长时(比如大批量 I/O 或大量矩阵变换等CPU密集操作),整个系统都会等待这个操作,程序就不能响应键盘、鼠标、菜单等操作,而使用多线程技术可将耗时长的操作置于一个新的线程,从而不会影响到 主 GUI 线程,从而避免上述问题。

2、使多核心 CPU 系统更加有效;

当线程数不大于CPU核数时,操作系统可以调度不同的线程运行于不同的CPU核上。

3、改善程序结构;

一个既长又复杂的进程可以考虑分为多个线程,成为独立或半独立的运行部分,这样有利于程序的理解和维护。

创建一个界面,界面 show 以后调用睡眠函数,观察效果。
在这里插入图片描述

QThread 类

相关接口

Public Functions:
	QThread(QObject *parent = 0); 			// 构造函数  // pthread_create
	bool 	isFinished() const;  			// 判断线程是否退出
	bool	wait(unsigned long time = ULONG_MAX);   // pthread_join(&id)
	// 等待某个线程结束,最多等待time ms,如果时间没有设置,那么永远等待。

Public Slots:
	void	start(Priority priority = InheritPriority)  // 启动线程必须使用start
	void	terminate();				// 杀死线程  	// pthread_cancel

Static Public Members:
	Qt::HANDLE	currentThreadId() [static] 		// 得到当前执行者线程ID,可以直接qDebug
	void	sleep(unsigned long secs) [static]
	void	msleep(unsigned long msecs) [static]
	void	usleep(unsigned long usecs) [static]

	睡眠函数不能在主线程调用,会造成界面卡死。

Protected Functions:	
	virtual void run();  	// 启动新线程不能直接调用run,需要调用 start 接口,
							// start 会启动新线程,然后执行run里的代码块。

编程流程

1)子类化 QThread(重写一个类,继承自 QThread);
2)重写 run 函数,执行耗时操作(run 函数内有一个 while / for 循环 或 sleep);
3)子线程类实现 公共方法,供主线程传参(主线程给子线程传参);
4)主线程内 定义并实例化子线程的类对象;
5)主线程调用 start 方法 启动子线程;
6)设置必要的 信号和槽 做连接 ——> 子线程给主线程传参;
7)设置一个标记 来控制循环的退出,或者父线程调用 terminate 停止子线程。
注意:子线程内不允许操作界面上的任何部件,所有界面操作都应该由 GUI 主线程来进行

💡 练习

重写一个线程子类,GUI 线程传递一个值给子线程。界面放置一个按钮,点击按钮后线程开始运行。子线程打印主线程传递的值以后睡眠任意时间,线程运行结束后通知主线程自己运行的时间,主线程把子线程睡眠的总时间显示到界面上。
在这里插入图片描述

mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QThread>
#include <qdebug.h>

class MyThread : public QThread					// 1)重写一个类,继承自 QThread
{
    Q_OBJECT
public:
    explicit MyThread(QObject *parent = 0);
    void setValue(int num) {this->val = num;}	// 3)子线程类实现 公共方法,供主线程传参

signals:
    void valueSignal(int);						// 6)子线程发信号给主线程

public slots:

private:
    void run();									// 2)重写 run 函数
    int val;
};

#endif // MYTHREAD_H
mythread.cpp
#include "mythread.h"

MyThread::MyThread(QObject *parent) :
    QThread(parent)
{
}

void MyThread::run()					// 2)重写 run 函数
{
    qDebug() << "From parent thread: val = " << val;
    QThread::sleep(16);					// 2)run 函数内有一个 while/for 循环 或 sleep
    emit valueSignal(16);
}
widget.h
#define WIDGET_H

#include <QtWidgets>
#include "mythread.h"

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

private:
    Ui::Widget *ui;
    MyThread *myth;							// 4)主线程内 定义子线程的类对象

public slots:
    void valueSlot(int);					// 6)主线程的槽接收来自子线程的信号

private slots:
    void on_pushButton_clicked();
};

#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    // 4)主线程内 实例化子线程的类对象
    myth = new MyThread(this);          // 使用 this 的目的仅是方便回收

    myth->setValue(98);					// 4.5)主线程传参给子线程

    // 6)设置必要的 信号和槽 做连接
    QObject::connect(myth, SIGNAL(valueSignal(int)), this,  SLOT(valueSlot(int)));
}

Widget::~Widget()
{
    delete ui;
}

void Widget::valueSlot(int num)			// 6)子线程给主线程传参的方式;信号与槽
{
    ui->label->setText("From child thread: val = " + QString::number(num));
}

void Widget::on_pushButton_clicked()
{
    myth->start();						// 5)主线程调用 start 方法 启动子线程
}

实现效果如下:
在这里插入图片描述

💡 进度条显示拷贝进度(verson 2)

newthread.h
#ifndef NEWTHREAD_H
#define NEWTHREAD_H

#include <QThread>
#include <QtWidgets>

class NewThread : public QThread
{
    Q_OBJECT
public:
    explicit NewThread(QObject *parent = 0);
    void setFileNames(QString src, QString dest)
    {
        srcName = src;
        destName = dest;
    }

signals:
    void valueSignal(int);

public slots:

protected:
    void run();
    QString srcName;
    QString destName;
    int percentage;
};

#endif // NEWTHREAD_H
newthread.cpp
#include "newthread.h"

#define KB 1024

NewThread::NewThread(QObject *parent) :
    QThread(parent)
{
}

void NewThread::run()
{
    QFile srcFile(srcName);
    if (!srcFile.open(QIODevice::ReadOnly))
    {
        qDebug() << "打开文件失败";
        return ;
    }

    QFile destFile(destName);
    if (!destFile.open(QIODevice::ReadWrite | QIODevice::Truncate))
    {
        qDebug() << "复制文件失败";
        return ;
    }

    qint64 totalSize = srcFile.size();
    qint64 currentSize = 0;
    QByteArray buffer;

    while (!srcFile.atEnd())
    {
        buffer.clear();
        buffer = srcFile.read(KB);
        destFile.write(buffer);

        currentSize += buffer.size();
        percentage = 100 * currentSize / totalSize;
        emit valueSignal(percentage);
    }
}
widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QtWidgets>
#include "newthread.h"

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

private slots:
    void on_pushButton_released();

private:
    Ui::Widget *ui;
    NewThread *thr;

public slots:
    void valueSlot(int);
};

#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    ui->progressBar->setValue(0);

    thr = new NewThread(this);
    QObject::connect(thr, SIGNAL(valueSignal(int)), this,  SLOT(valueSlot(int)));
}

Widget::~Widget()
{
    delete ui;
}

void Widget::on_pushButton_released()
{
    QString srcName = QFileDialog::getOpenFileName(this, tr("选择文件"), \
    							"D:/Packages_", tr("all  (*.*)"));
    if (srcName.isEmpty())
        return ;
    qDebug() << "src: " << srcName;

    QString destName = QFileDialog::getSaveFileName(this, tr("保存文件"), \
    							"e:", tr("all  (*.*)"));
    if (destName.isEmpty())
        return ;
    qDebug() << "dest: " << destName;

    ui->progressBar->setValue(0);				// 每次启动线程前,将进度条的进度 置0
    thr->setFileNames(srcName, destName);
    thr->start();								// 不要忘记启动线程
}

void Widget::valueSlot(int value)
{
    ui->progressBar->setValue(value);
}

运行结果如下:(复制文件时,拖动窗口不会产生卡顿)
在这里插入图片描述

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

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

相关文章

力扣经典面试题——合并区间

合并区间 https://leetcode.cn/problems/merge-intervals/description/?envTypestudy-plan-v2&envIdtop-interview-150 这题思维量一般但比较考察API的使用。 1、数组的自定义排序 2、数组的初始化定义 3、Arrays转int 通过重写Comparator的compare方法来自定义排序规则…

SpringBoot热部署

SpringBoot热部署 借鉴链接&#x1f517;&#xff1a;SpringBoot中的热部署 添加devtools依赖和pom插件 <!-- devtools 依赖 --> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId&…

低代码核心能力表单引擎可以提高业务处理效率,降低成本的

在数字化时代&#xff0c;企业面临着海量的数据和复杂的业务需求&#xff0c;对于低代码表单的需求也逐渐增加&#xff0c;低代码表单可以提高企业的业务处理效率&#xff0c;还可以降低开发成本&#xff0c;缩短开发周期。 低代码表单应用场景​ 低代码的表单主要用于数据采集…

如何使用SOLIDWORKS在实体中快速拾取多个曲面

在 SOLIDWORKS 建模等操作中&#xff0c;很多操作中会需要选择多个曲面。这时候很多人会想到通过 control 键和鼠标的同时操作来完成曲面的选择。但是这种方法比较慢&#xff0c;而且碰到大量曲面的时候怎么办&#xff1f;很多用过PROE的人也会提出在 SOLIDWORKS 中有没有像PRO…

Anaconda入门使用指南完整版,Python零基础入门必看系列

文章目录 前言一、为什么选择Anaconda&#xff1f;1.1 什么是 Anaconda&#xff1f;1.2 什么是 conda &#xff1f;1.3 Anaconda 的优点&#xff1f; 二、如何安装Anaconda&#xff1f;三、如何管理Python包&#xff1f;四、如何管理Python环境&#xff1f;关于Python技术储备一…

一图看懂!生成式AI 算法现状

截至2023年8月&#xff0c;在算法备案系统登记的相关算法已经有151个&#xff0c;我们可以观察到All in AI的中国公司布局生成式AI算法的现状。在这151个算法中&#xff0c;33.8%的生成合成式算法都集中在文本生成领域&#xff0c;而图像、多媒体和音频方向也是各家的主攻方向之…

图像清晰度 和像素、分辨率、镜头的关系

关于图像清晰度的几个知识点分享。 知识点 清晰度 清晰度指影像上各细部影纹及其边界的清晰程度。清晰度&#xff0c;一般是从录像机角度出发&#xff0c;通过看重放图像的清晰程度来比较图像质量&#xff0c;所以常用清晰度一词。 而摄像机一般使用分解力一词来衡量它“分解被…

代码随想录27期|Python|Day13|栈与队列|239. 滑动窗口最大值 (一刷至少需要理解思路)|347.前 K 个高频元素 (一刷至少需要理解思路)

239. 滑动窗口最大值 单调队列 滑动窗口中的队列一直保持出口大&#xff0c;入口小的顺序。&#xff08;图&#xff1a;代码随想录&#xff09; 1、每次有新的元素进入&#xff08;也就是滑动窗口移动后&#xff09;&#xff0c;都需要先和入口的元素比较大小&#xff0c;如果…

cadence中如何在更新原理图封装

cadence中如何在更新原理图封装 一、更改原理图封装 当原理图画好后&#xff0c;如果我们发现某个封装错了&#xff0c;需要改动&#xff0c;需要找到你最初画Library中器件封装文件打开&#xff0c;进行修改&#xff0c;修改好后保存。 二、更新封装 保存好后&#xff0c;…

基于Java SSM框架实现高校人事管理系统项目【项目源码】计算机毕业设计

基于java的SSM框架实现高校人事管理系统演示 JSP技术介绍 JSP技术本身是一种脚本语言&#xff0c;但它的功能是十分强大的&#xff0c;因为它可以使用所有的JAVA类。当它与JavaBeans 类进行结合时&#xff0c;它可以使显示逻辑和内容分开&#xff0c;这就极大的方便了用户的需…

各个数据库存二进制大文件性能测试

1前言 ​ 有个项目软件前端将二进制大文件存在了indexDB,每次给后端传文件&#xff08;需要传到底层C进行调用&#xff09;都会导致内存占用飙升&#xff0c;想着使用前后端都能共同操作的数据库来解决这个内存占用的问题&#xff0c;并且希望这个更具尽可能的轻量&#xff0c…

【文末送书】拥抱智能驾驶

欢迎关注博主 Mindtechnist 或加入【智能科技社区】一起学习和分享Linux、C、C、Python、Matlab&#xff0c;机器人运动控制、多机器人协作&#xff0c;智能优化算法&#xff0c;滤波估计、多传感器信息融合&#xff0c;机器学习&#xff0c;人工智能等相关领域的知识和技术。关…

总结6种@Transactional注解的失效场景

作者简介&#xff1a;大家好&#xff0c;我是smart哥&#xff0c;前中兴通讯、美团架构师&#xff0c;现某互联网公司CTO 联系qq&#xff1a;184480602&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗互联网寒冬 引言 昨天有粉丝咨询了…

海豚²来了丨DolphinDB 集成 DolphinScheduler,任务调度更轻松

DolphinDB 是一款高性能时序数据库。DolphinDB 集成了功能强大的编程语言和高容量高速度的批流一体数据分析系统&#xff0c;为海量数据&#xff08;特别是时间序列数据&#xff09;的快速存储、检索、计算及分析提供一站式解决方案。在实际生产环境中&#xff0c;经常存在数据…

以太坊:前世今生与未来

一、引言 以太坊&#xff0c;这个在区块链领域大放异彩的名字&#xff0c;似乎已经成为了去中心化应用&#xff08;DApps&#xff09;的代名词。从初期的萌芽到如今的繁荣发展&#xff0c;以太坊经历了一段曲折而精彩的旅程。让我们一起回顾一下以太坊的前世今生&#xff0c;以…

cordic 算法学习记录

参考&#xff1a;b站教学视频FPGA&#xff1a;Cordic算法介绍与实现_哔哩哔哩_bilibili FPGA硬件实现加减法、移位等操作比较简单&#xff0c;但是实现乘除以及函数计算复杂度高且占用资源多&#xff0c;常见的计算三角函数/平方根的求解方式有①查找表&#xff1a;先把函数对应…

解决夜神模拟器与Android studio自动断开的问题

原因&#xff1a;夜神模拟器的adb版本和Android sdk的adb版本不一致 解决办法&#xff1a; 1.找到android的sdk &#xff08;1&#xff09;File--->Project Structure (2)SDK Location:记下sdk的位置 2.找到sdk中的adb文件 SDK-->platform-tools-->adb.exe 3.复制…

轻松管理远程设备,Termius for Mac助你畅享多协议远程管理

在当今数字化时代&#xff0c;远程管理已经成为许多人日常工作中不可或缺的一部分。无论你是IT专业人士、开发者还是网络管理员&#xff0c;都需要一个强大而可靠的远程管理工具。而Termius for Mac&#xff08;多协议远程管理软件&#xff09;正是你的最佳选择。 Termius for…

三翼鸟2023辉煌收官, 定盘2024高质量棋局

最近在不同平台上接连看到这样的热搜话题&#xff1a;用时间胶囊记录2023的自己、2023年度问答、2023十大网络流行语公布… 显然&#xff0c; 2023年进入最后一个月&#xff0c;时间匆匆&#xff0c;这也意味着又到了总结过去和规划未来的时候。拿到结果、取得成绩当然是对202…

语音验证码的使用场景

相较于短信验证&#xff0c;语音验证是一种特殊的验证方式&#xff0c;目前在“用户注册”场景下更多的是作为短信验证码的一种补充&#xff0c;当收不到短信时&#xff0c;用户可以选择接听电话的方式来获取验证码&#xff0c;最大程度上避免用户流失。 在一些需要验证用户身…