练习1:Qt 进度条与多线程应用
题目描述
开发一个基于 Qt 的应用程序,该应用程序包含一个水平进度条(
QSlider
),并且需要通过多线程来更新进度条的值。请根据以下要求完成代码:
界面设计:
使用
QSlider
控件作为进度条。设置
QSlider
的样式多线程更新:
创建一个自定义线程类
mythread
,该线程类继承自QThread
。使主线程接收到信号后,更新
QSlider
的值。信号与槽:
使用信号与槽机制实现线程与主线程之间的通信。
当线程中的值发生变化时,通过信号通知主线程更新进度条。
1.mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include<QThread>
class mythread : public QThread
{
Q_OBJECT
public:
explicit mythread(QObject *parent = nullptr);
void run() override; // 线程的执行函数
signals:
void updateValue(int value); // 发送信号更新进度条
};
#endif // MYTHREAD_H
2.mythread.cpp
#include "mythread.h"
#include<QThread>
mythread::mythread(QObject *parent){}
void mythread::run()
{
int value = 0;
while (true) {
emit updateValue(value);
value = (value + 1) % 101; // 0 ~ 100 循环
QThread::msleep(100); // 休眠 100ms
}
}
3.widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QWidget>
#include <QMovie>
#include <QThread>
#include <QTextEdit>
#include <QWidget>
#include <QThread>
#include<QScreen>
#include<QLabel>
#include<QDebug>
#include<QPixmap>
#include<QApplication>
#include<QPushButton>
#include "mythread.h"
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public slots:
void setSliderValue(int value); // 更新进度条
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
Ui::Widget *ui;
mythread *thread;
};
#endif // WIDGET_H
4.widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QString qss = "QSlider { background: transparent; }"//设置 QSlider 的背景为透明
"QSlider::groove:horizontal { border: 1px solid gray; background: lightgray; height: 15px; border-radius: 5px; }"
"QSlider::sub-page:horizontal { background: #B5E61D; border-radius: 5px; }"
"QSlider::handle:horizontal { background:#5A730E; width: 10px;border-radius: 5px }";
ui->horizontalSlider->setStyleSheet(qss);
// 创建并启动线程
thread = new mythread(this);
connect(thread, &mythread::updateValue, this, &Widget::setSliderValue);
thread->start();
}
Widget::~Widget()
{
delete ui;
}
// 槽函数:更新进度条
void Widget::setSliderValue(int value)
{
ui->horizontalSlider->setValue(value);
}
练习2:基于 Qt 的文件复制工具开发
开发一个基于 Qt 的文件复制工具,要求实现以下功能:
文件选择:
使用
QFileDialog
选择源文件和目标文件。支持选择大文件(超过 800MB)。
文件复制:
使用 Qt 的文件 IO 操作(
QFile
)实现文件复制功能。支持分块读取和写入文件,避免一次性加载大文件到内存中。
多线程处理:
使用
QThread
在后台执行文件复制操作,避免阻塞主线程。通过信号与槽机制,将复制进度实时传递给主线程。
进度显示:
使用自定义的
QSlider
作为进度条,显示文件复制的进度。自定义
QSlider
的样式,使其外观美观。错误处理:
如果文件打开失败或复制失败,弹出错误提示框。
如果复制完成,弹出提示框显示“文件复制完成”。
1.mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
#include <QString>
class MyThread : public QThread
{
Q_OBJECT
public:
explicit MyThread(const QString &source, const QString &destination, QObject *parent = nullptr);
signals:
void progressUpdated(int value); // 信号:用于更新进度条的值
protected:
void run() override; // 线程执行函数
private:
QString sourcePath; // 源文件路径
QString destinationPath; // 目标文件路径
};
#endif // MYTHREAD_H
2.mythread.cpp
#include "mythread.h"
#include <QFile>
#include <QDebug>
MyThread::MyThread(const QString &source, const QString &destination, QObject *parent)
: QThread(parent), sourcePath(source), destinationPath(destination)
{
}
void MyThread::run()
{
QFile sourceFile(sourcePath);
QFile destinationFile(destinationPath);
// 打开源文件
if (!sourceFile.open(QIODevice::ReadOnly)) {
emit progressUpdated(-1); // 发送错误信号
return;
}
// 打开目标文件
if (!destinationFile.open(QIODevice::WriteOnly)) {
emit progressUpdated(-1); // 发送错误信号
return;
}
qint64 fileSize = sourceFile.size(); // 获取文件大小
qint64 bytesCopied = 0; // 已复制的字节数
char buffer[4096]; // 缓冲区
// 分块读取和写入文件
while (!sourceFile.atEnd()) {
qint64 bytesRead = sourceFile.read(buffer, sizeof(buffer)); // 读取数据
destinationFile.write(buffer, bytesRead); // 写入数据
bytesCopied += bytesRead; // 更新已复制的字节数
int progress = static_cast<int>((bytesCopied * 100) / fileSize); // 计算进度
emit progressUpdated(progress); // 发送进度信号
}
// 关闭文件
sourceFile.close();
destinationFile.close();
}
3.widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QSlider>
#include <QFileDialog>
#include <QMessageBox>
#include "mythread.h"
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void updateProgress(int value); // 槽函数:更新进度条
private:
Ui::Widget *ui;
MyThread *thread; // 文件复制线程
};
#endif // WIDGET_H
4.widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QFileDialog>
#include <QMessageBox>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 设置 QSlider 的样式
QString qss = "QSlider { background: transparent; }" // 设置 QSlider 的背景为透明
"QSlider::groove:horizontal { border: 1px solid gray; background: lightgray; height: 15px; border-radius: 5px; }"
"QSlider::sub-page:horizontal { background: #B5E61D; border-radius: 5px; }"
"QSlider::handle:horizontal { background:#5A730E; width: 10px; border-radius: 5px; }";
ui->horizontalSlider->setStyleSheet(qss);
ui->horizontalSlider->setRange(0, 100); // 设置进度条范围
ui->horizontalSlider->setValue(0); // 初始值为 0
// 选择源文件
QString sourceFile = QFileDialog::getOpenFileName(this, "选择要复制的文件");
if (sourceFile.isEmpty()) {
QMessageBox::warning(this, "警告", "未选择源文件");
return;
}
// 选择目标文件
QString destinationFile = QFileDialog::getSaveFileName(this, "选择保存位置");
if (destinationFile.isEmpty()) {
QMessageBox::warning(this, "警告", "未选择目标文件");
return;
}
// 创建并启动线程
thread = new MyThread(sourceFile, destinationFile, this);
connect(thread, &MyThread::progressUpdated, this, &Widget::updateProgress);
thread->start();
}
Widget::~Widget()
{
if (thread) {
thread->quit(); // 停止线程
thread->wait(); // 等待线程结束
delete thread; // 释放线程对象
}
delete ui;
}
// 槽函数:更新进度条
void Widget::updateProgress(int value)
{
if (value == -1) {
QMessageBox::critical(this, "错误", "文件复制失败");
return;
}
ui->horizontalSlider->setValue(value); // 更新 QSlider 的值
if (value == 100) {
QMessageBox::information(this, "完成", "文件复制完成");
}
}