一、QFileDialog 文件选择对话框
QFileDialog是一个内置的文件选择对话框,继承自QDialog,这样类的特点是使用静态成员函数弹出对话框,对话框选择文件路径使用返回值返回。
获取要打开单文件的文件路径
获得一个你要写出的文件路径
参数1:父对象
参数2:标题,实际上就是windowTitle属性
参数3:基目录,即在哪个目录下打开对话框,默认值表示工作目录
参数4:文件类型过滤器
返回值:选中的文件路径,如果用户不选择就关闭,则返回""
需要注意的是,QFileDialog类本身不具备任何读写能力,只是获取一个要读写的文件路径而已。
示例代码下载链接:百度网盘 请输入提取码
提取码:hqyj
--来自百度网盘超级会员V6的分享
textBrowserOpen组件支持大量文本的显示 比Qlable好用 前者支持append向后追加 后者会覆盖掉以前的东西
dialog.cpp
#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
group = new QButtonGroup(this);
group->addButton(ui->pushButtonOpen,1);
group->addButton(ui->pushButtonSave,2);
group->addButton(ui->pushButtonCopy,3);
connect(group,SIGNAL(buttonClicked(int)),
this,SLOT(btnsClickedSlot(int)));
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::btnsClickedSlot(int id)
{
if(id == 1)
{
QString path = QFileDialog::getOpenFileName(this,
"打开",
"E:/Image/",
"头文件(*.h);;源文件(*.cpp *.c);;所有文件(*.*)");
ui->textBrowserOpen->append(path);//显示到这个上面
}else if(id == 2)
{
QString path = QFileDialog::getSaveFileName(this,
"保存",
"E:/",
"头文件(*.h);;源文件(*.cpp *.c);;所有文件(*.*)");
ui->textBrowserSave->append(path);
}else if(id==3)
{
}
}
dailog.h
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include <QDebug>
#include <QButtonGroup>//按钮组 超过两个就用按钮组
#include <QFileDialog> // 文件选择对话框
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
private:
Ui::Dialog *ui;
QButtonGroup *group;
private slots:
// 按钮组点击的槽函数 发送的是编号
void btnsClickedSlot(int);
};
#endif // DIALOG_H
二、QFileInfo 文件信息类
QFileInfo类可以通过大量的成员函数获得对应文件或目录的信息。
常用函数如下:
QFileInfo::QFileInfo(const QString & file)
构造函数,参数是文件的路径,如果路径不存在,仍然可以创建出对象。
QDateTime QFileInfo::created() const
QDateTime QFileInfo::lastModified() const
QDateTime QFileInfo::lastRead() const
获得文件的创建、上次修改与最后读取的日期和时间。
bool QFileInfo::exists() const
bool QFileInfo::exists(const QString & file) [static]
判断文件或目录是否存在。
bool QFileInfo::isDir() const
bool QFileInfo::isExecutable() const
bool QFileInfo::isFile() const
判断QFileInfo对象构建时传入的路径是否是目录、可执行文件或文件。
bool QFileInfo::isHidden() const
bool QFileInfo::isReadable() const
bool QFileInfo::isWritable() const
判断文件是否隐藏、可读或可写
qint64 QFileInfo::size() const
获得文件的大小,单位字节
示例代码下载链接:百度网盘 请输入提取码
提取码:hqyj
--来自百度网盘超级会员V6的分享
dialog.cpp
#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
group = new QButtonGroup(this);
group->addButton(ui->pushButtonOpen,1);
group->addButton(ui->pushButtonSave,2);
group->addButton(ui->pushButtonCopy,3);
connect(group,SIGNAL(buttonClicked(int)),
this,SLOT(btnsClickedSlot(int)));
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::btnsClickedSlot(int id)
{
if(id == 1)
{ //先临时变量
QString path = QFileDialog::getOpenFileName(this,
"打开",
"E:/Image/",
"头文件(*.h);;源文件(*.cpp *.c);;所有文件(*.*)");
//再用已经存下来的去看有没有 如果已经存在的是空的并且这一次也是空的 就弹这个错误提示框
if(readPath == "" && path == "")
{
QMessageBox::warning(this,"提示","请选择要读取的文件!");
return;
}
readPath = path;
// 清空之前信息显示
ui->textBrowserOpen->clear();
ui->textBrowserOpen->append(readPath);
// 显示要读取的文件信息
QFileInfo info(readPath);
// 获得创建时间 toString时间函数
QString text = info.created().toString("yyyy-MM-dd hh:mm:ss");
text.prepend("创建时间:");//往前追加
ui->textBrowserOpen->append(text);
// 判断是否可读
if(info.isReadable())
ui->textBrowserOpen->append("可读");
else
ui->textBrowserOpen->append("不可读");
// 获得文件大小
int count = 0; // 除了多少次
qint64 size = info.size();
while(size > 1024)
{
size = size/1024;
count++;
}
if(count == 0)
{
text = "字节";
}else if(count == 1)
{
text = "kb";
}else if(count == 2)
{
text = "Mb";
}else if(count == 3)
{
text = "Gb";
}
// 数字转字符串
text.prepend(QString::number(size)).prepend("文件大小:");
ui->textBrowserOpen->append(text);
}else if(id == 2)
{
QString path = QFileDialog::getSaveFileName(this,
"保存",
"E:/",
"头文件(*.h);;源文件(*.cpp *.c);;所有文件(*.*)");
if(writePath == "" && path == "")
{
QMessageBox::warning(this,"提示","请设置要保存的文件!");
return;
}
writePath = path;
// 清空之前信息显示
ui->textBrowserSave->clear();
ui->textBrowserSave->append(writePath);
}else if(id==3)
{
}
}
dialog.h
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include <QDebug>
#include <QButtonGroup>
#include <QFileDialog> // 文件选择对话框
#include <QFileInfo> // 文件信息
#include <QMessageBox>
#include <QDateTime>
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
private:
Ui::Dialog *ui;
QButtonGroup *group;
// 读写的路径 长期保存的
QString readPath;
QString writePath;
private slots:
// 按钮组点击的槽函数
void btnsClickedSlot(int);
};
#endif // DIALOG_H
三、QFile 文件读写类
QFile类用于文件读写,其间接基类是QIODevice,QIODevice类是Qt中所有IO操作的基类,内部规定了很多基础的IO相关接口。
QFile类常用函数如下:
这个类的对象可以使用栈内存 好处因为文件读写的这些对象占用的内存一般都是特别大的 如果要去长期持有不是特别好 所以用栈内存对象会好一些
QFile(const QString & name)
构造函数,参数是文件的路径。不传this 并没有真正的读写文件 下面的函数才是真正的去读写函数
bool QIODevice::open(OpenMode mode) [virtual]
以流式数据打开文件,参数是打开的模式:
QIODevice::ReadOnly 只读
QIODevice::WriteOnly 只写
QIODevice::ReadWrite 读写
bool QIODevice::atEnd() const [virtual]
是否已经读取到文件尾
QByteArray QIODevice::read(qint64 maxSize)
读取数据到字节数组中。
参数:读取的最大字节数
返回值:携带读取数据的字节数组类对象QByteArray 就相当于一个数组
qint64 QIODevice::write(const QByteArray & byteArray)
写出数据到到文件中。
参数:要写出的数据
返回值:实际写出的字节数
qint64 QIODevice::size() const [virtual]
获得还没读取的数据量,单位字节。
示例代码下载链接:百度网盘 请输入提取码
提取码:hqyj
--来自百度网盘超级会员V6的分享
四、UI与耗时操作
上面的文件拷贝代码中,如果拷贝的是大文件,在拷贝的过程中用户进行任何UI操作(例如:拖动窗口),程序都会进入未响应状态。原因是程序只有一个线程,也称之为主线程,此时主线程在执行拷贝的逻辑,因此无法及时响应UI操作,导致UI卡顿,造成假死。
如果程序在上述状态尝试关闭,Windows会检测到此程序的关闭操作无法及时响应,操作系统会进行干预,弹出程序未响应对话框,交给用户进行选择。
解决上述问题需要使用多线程,主线程用来操作UI,也被称为UI线程;所有的IO等的耗时操作都交给子线程处理,这样不会阻塞主线程执行程序的基本操作。
Qt中规定,UI操作只能在主线程执行,耗时操作只能在子线程执行。
五、多线程
5.1 复现未响应
使用线程类QThread的静态成员函数复现。
void QThread::msleep(unsigned long msecs) [static]
让cpu强行睡眠一段时间,参数是睡眠的时间,单位毫秒。
示例代码下载链接:百度网盘 请输入提取码
提取码:hqyj
--来自百度网盘超级会员V6的分享
dialog.cpp
#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
//链接 点击发射触发信号 信号槽接受并实现睡眠函数
connect(ui->pushButtonSleep,SIGNAL(clicked()),
this,SLOT(btnSleepClickedSlot()));
//链接 点击发射触发信号 信号槽接受并实现弹窗
connect(ui->pushButtonBox,SIGNAL(clicked()),
this,SLOT(btnBoxClickedSlot()));
}
Dialog::~Dialog()
{
delete ui;
}
//这种现象就是 点击睡眠按钮 线程去执行睡眠 你再点击弹窗会没有相应
void Dialog::btnSleepClickedSlot()
{
QThread::msleep(10000);//睡眠10s
}
void Dialog::btnBoxClickedSlot()
{
QMessageBox::information(this,"提示","你好!");
}
dialog.h
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include <QThread>//线程类
#include <QMessageBox>
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
private:
Ui::Dialog *ui;
private slots:
void btnSleepClickedSlot();//功能敲击按钮睡眠
void btnBoxClickedSlot();//功能敲击按钮弹窗
};
#endif // DIALOG_H
5.2 创建子线程
自定义线程类是Qt的非组件或窗口类,因此只有头文件和源文件,没有界面文件。
创建步骤如下:
1. 在Qt Creator中选中项目名称,鼠标右键,点击“添加新文件”。
2. 在弹出的窗口中,按照下图所示进行操作。
3. 在弹出的窗口中,先输入类名,再输入基类名(QThread),最后选择类型信息为“继承自QObject”,完成上述操作后,点击“下一步”。
4. 在项目管理界面,直接点击完成。可以看到对应头文件和源文件。
5. 覆盖基类QThread的run函数。
void QThread::run() [virtual protected]
此函数是线程的起始点,在线程对象调用start函数后自动执行。
6. 在主线程中创建子线程对象,并调用start函数启动线程。
void QThread::start(Priority priority = InheritPriority) [slot]
启动子线程,参数是优先级的枚举值,优先级越高cpu会更加侧重于此线程的执行。
示例代码下载链接:https://pan.baidu.com/s/1Vv6KD6F6jJZsSD_KyNHVzQ
提取码:hqyj
--来自百度网盘超级会员V6的分享
dialog.h
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include <QThread>//线程类
#include <QMessageBox>
#include "mythread.h" //自定义线程类
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
private:
Ui::Dialog *ui;
private slots:
void btnSleepClickedSlot();//功能敲击按钮睡眠
void btnBoxClickedSlot();//功能敲击按钮弹窗
};
#endif // DIALOG_H
myThread,h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
#include <QDebug>
class myThread : public QThread
{
Q_OBJECT
public:
explicit myThread(QObject *parent = 0);
//覆盖run函数
protected:
void run();//编程斜体就说明覆盖成功了
signals:
public slots:
};
#endif // MYTHREAD_H
dialog.cpp
#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
//链接 点击发射触发信号 信号槽接受并实现睡眠函数
connect(ui->pushButtonSleep,SIGNAL(clicked()),
this,SLOT(btnSleepClickedSlot()));
//链接 点击发射触发信号 信号槽接受并实现弹窗
connect(ui->pushButtonBox,SIGNAL(clicked()),
this,SLOT(btnBoxClickedSlot()));
}
Dialog::~Dialog()
{
delete ui;
}
//这种现象就是 点击睡眠按钮 线程去执行睡眠 你再点击弹窗会没有相应
void Dialog::btnSleepClickedSlot()
{
//QThread::msleep(10000);//睡眠10s
//以前是点击按钮执行主线程的睡眠函数 现在是点击按钮执行子线程程序
//创建子线程对象 必须引入头文件不然没法直接使用
myThread*mt = new myThread(this);
//调用start启动子线程
mt->start();
//上面这样做的目的就是两个按钮由两个线程控制各自执行各自的互不影响
}
void Dialog::btnBoxClickedSlot()
{
QMessageBox::information(this,"提示","你好!");
}
myThread.cpp
#include "mythread.h"
myThread::myThread(QObject *parent) :
QThread(parent)
{
}
void myThread::run()
{
//制定耗时操作
QThread::msleep(10000);//睡眠10s
//执行完了子线程,就显示一下
qDebug() >> "子线程执行完毕";
}
5.3 异步刷新
5.2节中的代码虽然各个线程之间互不影响,但是也互相没有任何关联。在实际开发中,比较常见的使用场景是子线程执行耗时操作,把耗时操作的相关信息发送给主线程,主线程可以在UI上显示这些信息。(
此时可以在子线程中发射自定义信号到主线程的操作函数,实现两个线程之间的通信。
示例代码下载链接:https://pan.baidu.com/s/1uKd0oqV0IWVGT7sikG_Ewg
提取码:hqyj
--来自百度网盘超级会员V6的分享
dialog.h
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include <QThread>//线程类
#include <QMessageBox>
#include "mythread.h" //自定义线程类
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
private:
Ui::Dialog *ui;
myThread*mt;
private slots:
void btnSleepClickedSlot();//功能敲击按钮睡眠
void btnBoxClickedSlot();//功能敲击按钮弹窗
void finishedSlot(QString);//接受子线程发射的信号
};
#endif // DIALOG_H
myThread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
#include <QDebug>
class myThread : public QThread
{
Q_OBJECT
public:
explicit myThread(QObject *parent = 0);
//覆盖run函数
protected:
void run();//编程斜体就说明覆盖成功了
signals:
//自定义信号 子线程执行结束发射的信号 传参无所谓
void finishedSignal(QString);
public slots:
};
#endif // MYTHREAD_H
dialog.cpp
#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
//链接 点击发射触发信号 信号槽接受并实现睡眠函数
connect(ui->pushButtonSleep,SIGNAL(clicked()),
this,SLOT(btnSleepClickedSlot()));
//链接 点击发射触发信号 信号槽接受并实现弹窗
connect(ui->pushButtonBox,SIGNAL(clicked()),
this,SLOT(btnBoxClickedSlot()));
}
Dialog::~Dialog()
{
delete ui;
}
//这种现象就是 点击睡眠按钮 线程去执行睡眠 你再点击弹窗会没有相应
void Dialog::btnSleepClickedSlot()
{
//QThread::msleep(10000);//睡眠10s
//以前是点击按钮执行主线程的睡眠函数 现在是点击按钮执行子线程程序
//创建子线程对象 必须引入头文件不然没法直接使用
mt = new myThread(this);
//调用start启动子线程
mt->start();
//上面这样做的目的就是两个按钮由两个线程控制各自执行各自的互不影响
//注意如果启动多个子线程就乱了 怎么避免写个屏蔽按钮 防止多个子线程混乱
ui->pushButtonSleep->setEnabled(false);
}
void Dialog::btnBoxClickedSlot()
{
//QMessageBox::information(this,"提示","你好!");
//建立两个线程之间信号槽的链接
//过程就是 你点击睡眠按钮 子线程就是睡眠10秒睡眠结束发送信号给主线程 主线程收到信号并弹窗显示
connect(mt,SIGNAL(finishedSignal(QString)),
this,SLOT(finishedSlot(QString)));
}
void Dialog::finishedSlot(QString text)
{
//弹窗展示子线程结束后发射的信息
QMessageBox::information(this,"提示",text);
// 回复按钮 下一次还可以点击开始睡眠
ui->pushButtonSleep->setEnabled(true);
}
myThread.cpp
#include "mythread.h"
myThread::myThread(QObject *parent) :
QThread(parent)
{
}
void myThread::run()
{
//制定耗时操作
QThread::msleep(10000);//睡眠10s
//执行完了子线程,就显示一下
qDebug() << "子线程执行完毕";
//发射信号。参数随便打。发出信号然后主线程来接受
emit finishedSignal("djsdjkaskd");
}
5.4 停止线程
使用下面的函数可以强行停止线程执行。
void QThread::terminate() [slot]
不建议强行停止线程,因为线程都是执行耗时操作的,绝大多数耗时操作占用的一些资源需要进行或关闭或释放,强行停止线程会导致关闭或释放等代码无法执行。
线程在执行完run函数后,会自动停止,因此可以间接地让线程今早执行完run函数,比较温柔地停止线程,例如可以给循环增加可控制的标志位,通过改变标志位的数值,使循环结束,从而执行循环体后的代码。
链接:百度网盘 请输入提取码
提取码:hqyj
--来自百度网盘超级会员V6的分享
【Day6作业】
把三、QFile 文件读写类的单线程文件拷贝代码
更改为多线程。
提示:
1. QFile的逻辑拿到run函数中
2. 主线程通过自定义线程类的构造函数传入路径
3. 在子线程通过信号槽发送拷贝的进度值到主线程刷新
copyThread.h
#ifndef COPYTHREAD_H
#define COPYTHREAD_H
#include <QThread>
// 头文件
#include <QFile>
class CopyThread : public QThread
{
Q_OBJECT
public:
explicit CopyThread(QString,QString,QObject *parent = 0);
protected:
void run();
private:
QString readPath;
QString writePath;
signals:
// 发射拷贝进度值的信号函数
void valueSignal(int);
public slots:
};
#endif // COPYTHREAD_H
dialog.h
#include <QFileInfo> // 文件信息
#include <QMessageBox>
#include <QDateTime>
#include "copythread.h" // 拷贝线程
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
private:
Ui::Dialog *ui;
QButtonGroup *group;
// 读写的路径
QString readPath;
QString writePath;
// 文件拷贝
void copy();
private slots:
// 按钮组点击的槽函数
void btnsClickedSlot(int);
// 接收子线程进度值的槽函数
void valueSlot(int);
};
#endif // DIALOG_H
copyThread.cpp
#include "copythread.h"
CopyThread::CopyThread(QString readPath,
QString writePath,QObject *parent) :
QThread(parent)
{
// 给传入的路径保存到属性中
this->readPath = readPath;
this->writePath = writePath;
}
void CopyThread::run()
{
// 创建两个QFile对象
QFile readFile(readPath);
QFile writeFile(writePath);
// 以只读和只写模式打开文件
readFile.open(QIODevice::ReadOnly);
writeFile.open(QIODevice::WriteOnly);
// 获取文件大小
qint64 totalSize = readFile.size();
// 已经读写的大小
qint64 hasReadSize = 0;
// 上一次的进度值
int lastPer = 0;
// 存储每次读取的数据的字节数组对象
QByteArray buffer;
// 循环读取
while(!readFile.atEnd())
{
// 读取最多1kb的数据到数组中
buffer = readFile.read(2048);
// 写出数据
hasReadSize += writeFile.write(buffer);
// 算百分比
int per = 100*hasReadSize/totalSize;
// 跟上一次的进度值比对
if(per!=lastPer)
{
// 发射进度值到主线程
emit valueSignal(per);
lastPer = per;
}
}
// 收尾
writeFile.flush();
writeFile.close();
readFile.close();
}
dialog.cpp
#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
group = new QButtonGroup(this);
group->addButton(ui->pushButtonOpen,1);
group->addButton(ui->pushButtonSave,2);
group->addButton(ui->pushButtonCopy,3);
connect(group,SIGNAL(buttonClicked(int)),
this,SLOT(btnsClickedSlot(int)));
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::copy()
{
// 是否已经选择读写的文件
if(readPath == "" || writePath == "")
{
QMessageBox::warning(this,"提示","请选择要读写的文件!");
return;
}
// 屏蔽拷贝按钮
ui->pushButtonCopy->setEnabled(false);
ui->pushButtonCopy->setText("拷贝中...");
// 开启子线程拷贝
CopyThread *ct = new CopyThread(readPath,writePath,this);
// 连接两个线程通信的信号槽
connect(ct,SIGNAL(valueSignal(int)),this,SLOT(valueSlot(int)));
ct->start();
}
void Dialog::btnsClickedSlot(int id)
{
if(id == 1)
{
QString path = QFileDialog::getOpenFileName(this,
"打开",
"E:/Image/",
"头文件(*.h);;源文件(*.cpp *.c);;所有文件(*.*)");
if(readPath == "" && path == "")
{
QMessageBox::warning(this,"提示","请选择要读取的文件!");
return;
}
readPath = path;
// 清空之前信息显示
ui->textBrowserOpen->clear();
ui->textBrowserOpen->append(readPath);
// 显示要读取的文件信息
QFileInfo info(readPath);
// 获得创建时间
QString text = info.created().toString("yyyy-MM-dd hh:mm:ss");
text.prepend("创建时间:");
ui->textBrowserOpen->append(text);
// 判断是否可读
if(info.isReadable())
ui->textBrowserOpen->append("可读");
else
ui->textBrowserOpen->append("不可读");
// 获得文件大小
int count = 0; // 除了多少次
qint64 size = info.size();
while(size > 1024)
{
size = size/1024;
count++;
}
if(count == 0)
{
text = "字节";
}else if(count == 1)
{
text = "kb";
}else if(count == 2)
{
text = "Mb";
}else if(count == 3)
{
text = "Gb";
}
// 数字转字符串
text.prepend(QString::number(size)).prepend("文件大小:");
ui->textBrowserOpen->append(text);
}else if(id == 2)
{
QString path = QFileDialog::getSaveFileName(this,
"保存",
"E:/",
"头文件(*.h);;源文件(*.cpp *.c);;所有文件(*.*)");
if(writePath == "" && path == "")
{
QMessageBox::warning(this,"提示","请设置要保存的文件!");
return;
}
writePath = path;
// 清空之前信息显示
ui->textBrowserSave->clear();
ui->textBrowserSave->append(writePath);
}else if(id==3)
{
copy();
}
}
void Dialog::valueSlot(int value)
{
// 更新进度条显示
ui->progressBar->setValue(value);
// 如果进度值=100,说明拷贝完成
if(value == 100)
{
// 回复拷贝按钮
ui->pushButtonCopy->setEnabled(true);
ui->pushButtonCopy->setText("开始拷贝");
QString text = "输出文件路径:";
text.append(writePath);
QMessageBox::information(this,"拷贝完成",text);
}
}