(7)Qt---文件IO

news/2024/2/29 2:56:56

目录

1. QFileDialog 文件选择对话框**

2. QFileInfo 文件信息类**

3. QFile 文件读写类***

4. UI与耗时操作**

5. QThread 线程类

5.1 复现阻塞

5.2 新建并启动子线程

5.3 异步刷新

5.4 停止线程

5.5 多线程文件拷贝


1. QFileDialog 文件选择对话框**

操作系统会提供一个统一样式的文件选择对话框,QFileDialog类可以直接调用系统自带的文件选择对话框。

QFileDialog继承自QDialog,这样的类直接使用静态成员函数调用。

// 参数1:父对象
// 参数2:标题栏
// 参数3:打开QFileDialog时所在的根目录,默认值为工作目录
// 参数4:文件格式过滤器
// 返回值:文件路径,选择失败返回空字符串
QString QFileDialog::getSaveFileName|getOpenFileName(QWidget * parent = 0, const QString & caption = QString(),const QString & dir = QString(),    const QString & filter = QString()) [static]

2. QFileInfo 文件信息类**

QFileInfo类用于获取文件信息。

相关函数如下:

// 构造函数
// 参数是文件路径,如果路径无效,对象仍然会创建成功
QFileInfo::QFileInfo(const QString & file)
// 判断对应路径下的文件是否存在
bool QFileInfo::exists() const
// 创建时间
QDateTime QFileInfo::created() const
// 上次修改时间
QDateTime QFileInfo::lastModified() const
// 上次读取时间
QDateTime QFileInfo::lastRead() const
// 是否可读
bool QFileInfo::isReadable() const
// 是否可写
bool QFileInfo::isWritable() const
// 获得文件大小,单位字节,如果文件不存在返回0
qint64 QFileInfo::size() const

更多API详见文档。

dialog.h

#ifndef DIALOG_H
#define DIALOG_H#include <QFileInfo>
#include <QtWidgets>
#include <QDialog>
#include <QButtonGroup>
#include <QDateTime>
namespace Ui {
class Dialog;
}class Dialog : public QDialog
{Q_OBJECTpublic:explicit Dialog(QWidget *parent = 0);~Dialog();private:Ui::Dialog *ui;QButtonGroup * group;//获得读取的路径、获得写出路径void getReadPath();void getWritePath();QString readPath; //要读取的路径QString writePath; //要存储的路径void showFileMessage();private slots://按钮组点击的槽函数void btnsClickedSlot(int);
};#endif // DIALOG_H

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::getReadPath()
{//过滤器QString filter = "所有格式(*.*);;C++(*.h *.cpp)";//四个参数,见笔记QString path = QFileDialog::getOpenFileName(this,"打开","E:/",filter);if(path==""&& readPath==""){//引导用户进行正确的处理QMessageBox::warning(this,"提示","请选择有效路径!");return;}readPath = path;//展示if(path != "")ui->textBrowserOpen->clear();ui->textBrowserOpen->append(readPath);
}void Dialog::getWritePath()
{//过滤器QString filter = "所有格式(*.*);;C++(*.h *.cpp)";//四个参数,见笔记QString path = QFileDialog::getSaveFileName(this,"打开","E:/",filter);if(path==""&& writePath==""){//引导用户进行正确的处理QMessageBox::warning(this,"提示","请选择有效路径!");return;}writePath = path;//展示if(path != "")ui->textBrowserOpen->clear();ui->textBrowserOpen->append(writePath);
}void Dialog::showFileMessage()
{//创建文件信息类对象QFileInfo info(readPath);//获得创建时间QString time = info.created().toString("yyyy-MM-dd hh:mm:ss");ui->textBrowserOpen->append(time.prepend("创建时间:"));//是否可读bool result = info.isReadable();if(result)ui->textBrowserOpen->append("可读");elseui->textBrowserOpen->append("不可读");//获取文件大小qint64 size = info.size();QString text;text.setNum(size).prepend("文件大小:").append("字节");ui->textBrowserOpen->append(text);
}void Dialog::btnsClickedSlot(int id)
{if(id==1){getReadPath();}else if(id==2){getWritePath();}else if(id==3){}
}

3. QFile 文件读写类***

用于读写文件,需要注意的是QFile间接继承QIODevice类,QIODevice类是Qt所有IO类的基类,QIODevice类中规定了一些最基础的IO接口,本节讲解的这些接口,在后续其它IO类中也可以使用。

QFile的常用函数如下:

// 参数为文件路径
QFile::QFile(const QString & name)
// 判断对应路径下的文件是否存在
bool QFile::exists() const
// 打开数据流
// 参数为打开模式
// 返回值为打开的结果
bool QIODevice::open(OpenMode mode) [virtual]
// 读取的数据流是否位于末端
bool QIODevice::atEnd() const [virtual]
// 读取maxSize个字节到返回值的字节数组对象中
// 注意maxSize表示最大数量,不一定是实际数量
QByteArray QIODevice::read(qint64 maxSize)
// 输出到制定的位置
// 参数是输出的内容
// 返回值是实际的输出量
qint64 QIODevice::write(const QByteArray & byteArray)
// 清空输出流的缓存数据,返回值是结果
bool QFileDevice::flush()
// 关闭数据流
void QIODevice::close() [virtual]
// 获取剩余可读的数据量
qint64 QIODevice::size() const [virtual]

dialog.h

#ifndef DIALOG_H
#define DIALOG_H#include <QFileInfo>
#include <QtWidgets>
#include <QDialog>
#include <QButtonGroup>
#include <QDateTime>
namespace Ui {
class Dialog;
}class Dialog : public QDialog
{Q_OBJECTpublic:explicit Dialog(QWidget *parent = 0);~Dialog();private:Ui::Dialog *ui;QButtonGroup * group;//获得读取的路径、获得写出路径void getReadPath();void getWritePath();QString readPath; //要读取的路径QString writePath; //要存储的路径void showFileMessage();void copy();private slots://按钮组点击的槽函数void btnsClickedSlot(int);
};#endif // DIALOG_H

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::getReadPath()
{// 过滤器QString filter = "所有格式(*.*);;C++(*.h *.cpp)";// 四个参数,见笔记QString path = QFileDialog::getOpenFileName(this,"打开","E:/",filter);if(path=="" && readPath==""){// 引导用户进行正确的处理QMessageBox::warning(this,"提示","请选择有效路径!");return;}readPath = path;// 展示if(path != "")ui->textBrowserOpen->clear();ui->textBrowserOpen->append(readPath);showFileMessage(); // 展示文件信息
}void Dialog::getWritePath()
{// 过滤器QString filter = "所有格式(*.*);;C++(*.h *.cpp)";// 四个参数,见笔记QString path = QFileDialog::getSaveFileName(this,"保存","E:/",filter);if(path=="" && writePath==""){// 引导用户进行正确的处理QMessageBox::warning(this,"提示","请选择有效路径!");return;}writePath = path;// 展示if(path != "")ui->textBrowserSave->clear();ui->textBrowserSave->append(writePath);
}void Dialog::showFileMessage()
{// 创建文件信息类对象QFileInfo info(readPath);// 获得创建时间QString time = info.created().toString("yyyy-MM-dd hh:mm:ss");ui->textBrowserOpen->append(time.prepend("创建时间:"));// 是否可读bool result = info.isReadable();if(result)ui->textBrowserOpen->append("可读");elseui->textBrowserOpen->append("不可读");// 获取文件大小qint64 size = info.size();QString text;text.setNum(size).prepend("文件大小:").append("字节");ui->textBrowserOpen->append(text);
}void Dialog::copy()
{// 读写的对象QFile readFile(readPath);QFile writeFile(writePath);if(!readFile.exists()){QMessageBox::warning(this,"提示","请选择有效的读写路径!");return;}// 屏蔽拷贝按钮ui->pushButtonCopy->setEnabled(false);ui->pushButtonCopy->setText("拷贝中");// 使用只读模式打开数据流readFile.open(QIODevice::ReadOnly);writeFile.open(QIODevice::WriteOnly);qint64 totalSize = readFile.size(); // 文件总大小qint64 hasCopied = 0; // 已经读写的数据量QByteArray buffer; // “小推车”while(!readFile.atEnd()) // 循环读取{buffer = readFile.read(1024); // 一次读取1kb// 输出hasCopied += writeFile.write(buffer);// 计算百分比int value = hasCopied*100/totalSize;// 更新UI显示ui->progressBar->setValue(value);}// 收尾writeFile.flush();writeFile.close();readFile.close();// 屏蔽拷贝按钮ui->pushButtonCopy->setEnabled(true);ui->pushButtonCopy->setText("开始拷贝");QMessageBox::information(this,"通知",writePath.prepend("文件拷贝完成,请查看"));
}void Dialog::btnsClickedSlot(int id)
{if(id == 1){getReadPath();}else if(id == 2){getWritePath();}else if(id == 3){copy();}
}

运行结果:

 

4. UI与耗时操作**

Qt项目在默认的情况下只有一个线程,这个线程负责UI显示与用户的人机交互等程序运行所需的基础操作,因此这个线程也被称为主线程。

但是如果在主线程中执行一些耗时操作(例如IO、复杂算法等),会导致主线程原本的操作阻塞,无法及时响应,因此出现程序假死的现象。

此时强行关闭程序,由于耗时操作正在处理,导致关闭的命令也无法及时执行,操作系统检测到某个应用程序关闭无效,启动操作系统的保护机制,弹出对话框窗口引导用户是否强行关闭。

 解决方法是新启动一个线程,让这个线程执行耗时操作,这样的线程被称为子线程。

5. QThread 线程类

5.1 复现阻塞

QThread类是Qt的线程类,其中使用睡眠函数可以非常方便的模仿耗时操作的效果。

// 使当前线程睡眠一段时间
// 参数为睡眠时间,单位毫秒
void QThread::msleep(unsigned long msecs) [static]

dialog.h

#ifndef DIALOG_H
#define DIALOG_H#include <QtWidgets>
//线程类头文件
#include <QThread>
namespace Ui {
class Dialog;
}class Dialog : public QDialog
{Q_OBJECTpublic:explicit Dialog(QWidget *parent = 0);~Dialog();private:Ui::Dialog *ui;private slots:void btnSleepClickedSlot();void btnBoxClickedSlot();
};#endif // DIALOG_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->pushButtonBox,SIGNAL(clicked()),this,SLOT(btnBoxClickedSlot()));connect(ui->pushButtonSleep,SIGNAL(clicked()),this,SLOT(btnSleepClickedSlot()));
}Dialog::~Dialog()
{delete ui;
}void Dialog::btnSleepClickedSlot()
{//睡眠10sQThread::msleep(10000);
}void Dialog::btnBoxClickedSlot()
{QMessageBox::information(this,"随便","无所谓");
}

5.2 新建并启动子线程

操作步骤如下所示:

1. 在Qt Creator中,选中项目名称,鼠标右键,点击“添加新文件”。

2. 在弹出的窗口中根据下图所示进行操作。

3. 在弹出的窗口中,依次输入类名(大驼峰)、基类名(QThread),选择类型信息(继承自QObject)后,点击“下一步”。

4. 在项目管理界面点击“完成”。可以在项目中看到已经创建的头文件和源文件。

5. 进入到自定义线程类的头文件,声明run函数。

// 子线程执行的起始点,在子线程对象调用start函数后,此函数自动被调用
// 此函数执行完成后,子线程也执行完成
void QThread::run() [virtual protected]

 

6. 在自定义线程类的源文件中,定义run函数。

7. 在run函数中添加子线程要执行的耗时操作代码。

8. 在主线程中创建子线程对象,调用start函数后开始子线程开始执行。

程序代码:

mythread.h

#ifndef MYTHREAD_H
#define MYTHREAD_H#include <QThread>
#include <QDebug>class MyThread : public QThread
{Q_OBJECT
public:explicit MyThread(QObject *parent = 0);protected:void run(); // 声明run函数 --1signals:public slots:};#endif // MYTHREAD_H

mythread.cpp

#include "mythread.h"MyThread::MyThread(QObject *parent) :QThread(parent)
{
}// 定义run函数 --2
void MyThread::run()
{qDebug() << "子线程开始" ;// 睡眠10smsleep(10000);qDebug() << "子线程结束" ;  //--3
}

dialog.h

#ifndef DIALOG_H
#define DIALOG_H#include <QtWidgets>
// 子线程头文件
#include "mythread.h"namespace Ui {
class Dialog;
}class Dialog : public QDialog
{Q_OBJECTpublic:explicit Dialog(QWidget *parent = 0);~Dialog();private:Ui::Dialog *ui;private slots:void btnSleepClickedSlot();void btnBoxClickedSlot();
};#endif // DIALOG_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->pushButtonBox,SIGNAL(clicked()),this,SLOT(btnBoxClickedSlot()));connect(ui->pushButtonSleep,SIGNAL(clicked()),this,SLOT(btnSleepClickedSlot()));
}Dialog::~Dialog()
{delete ui;
}void Dialog::btnSleepClickedSlot()
{// 创建子线程对象MyThread *mt = new MyThread(this);// 开始执行子线程mt->start();     //--5
}void Dialog::btnBoxClickedSlot()
{QMessageBox::information(this,"随便","无所谓");
}

运行结果:

5.3 异步刷新

在开发的过程中,经常遇到下面的场景:

子线程执行耗时操作,主线程显示耗时操作的信息。

此时需要子线程通过信号槽传参发送数据给主线程。需要注意的是,子线程只能执行耗时操作,主线程只能执行非耗时操作。

copythread.h

#ifndef COPYTHREAD_H
#define COPYTHREAD_H#include <QThread>class CopyThread : public QThread
{Q_OBJECT
public:explicit CopyThread(QObject *parent = 0);
//声明run函数
protected:void run();signals:// 自定义信号void valueSignal(int);public slots:};#endif // COPYTHREAD_H

copythread.cpp

#include "copythread.h"CopyThread::CopyThread(QObject *parent) :QThread(parent)
{
}
//定义run函数
void CopyThread::run()
{// 模拟文件拷贝的耗时操作for(int i=0;i<=100;i++){msleep(50); // 模拟拷贝消耗时间emit valueSignal(i); // 发射进度值}
}

dialog.h

#ifndef DIALOG_H
#define DIALOG_H#include <QDialog>
// 自定义线程类
#include "copythread.h"namespace Ui {
class Dialog;
}class Dialog : public QDialog
{Q_OBJECTpublic:explicit Dialog(QWidget *parent = 0);~Dialog();private:Ui::Dialog *ui;private slots:void btnClickedSlot(); // 按钮点击的槽函数void valueSlot(int); // 接收子线程信号参数的槽函数
};#endif // DIALOG_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->pushButton,SIGNAL(clicked()),this,SLOT(btnClickedSlot()));
}Dialog::~Dialog()
{delete ui;
}void Dialog::btnClickedSlot()
{// 创建并启动子线程CopyThread *ct = new CopyThread(this);// 连接两个线程之间的信号槽connect(ct,SIGNAL(valueSignal(int)),this,SLOT(valueSlot(int)));ct->start();
}void Dialog::valueSlot(int value)
{// 更新进度条ui->progressBar->setValue(value);
}

ui界面:

运行结果:

 

5.4 停止线程

介绍两种停止线程运行的方式:

  • 调用terminate函数,不推荐使用,因为这种方式是强制停止线程运行,可能导致占用的资源无法释放。
void QThread::terminate() [slot]
  • 使用标志位,耗时操作往往伴随着循环,可以给循环体增加一个标志位,通过控制这个标志位的值使循环正常跳出,从而使run函数正常结束。

 copythread.h

#ifndef COPYTHREAD_H
#define COPYTHREAD_H#include <QThread>class CopyThread : public QThread
{Q_OBJECT
public:explicit CopyThread(QObject *parent = 0);bool isRunning = true; // 是否运行的标志位,先不封装了protected:void run();signals:// 自定义信号void valueSignal(int);public slots:};#endif // COPYTHREAD_H

copythread.cpp

#include "copythread.h"CopyThread::CopyThread(QObject *parent) :QThread(parent)
{
}void CopyThread::run()
{// 模拟文件拷贝的耗时操作for(int i=0;i<=100 && isRunning;i++){msleep(50); // 模拟拷贝消耗时间emit valueSignal(i); // 发射进度值}
}

dialog.h

#ifndef DIALOG_H
#define DIALOG_H#include <QDialog>
// 自定义线程类
#include "copythread.h"namespace Ui {
class Dialog;
}class Dialog : public QDialog
{Q_OBJECTpublic:explicit Dialog(QWidget *parent = 0);~Dialog();private:Ui::Dialog *ui;CopyThread *ct = NULL;private slots:void btnClickedSlot(); // 按钮点击的槽函数void valueSlot(int); // 接收子线程信号参数的槽函数void btnFClickedSlot(); // 标志位停止的槽函数
};#endif // DIALOG_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->pushButton,SIGNAL(clicked()),this,SLOT(btnClickedSlot()));
}Dialog::~Dialog()
{delete ui;
}void Dialog::btnClickedSlot()
{// 创建并启动子线程ct = new CopyThread(this);// 连接两个线程之间的信号槽connect(ct,SIGNAL(valueSignal(int)),this,SLOT(valueSlot(int)));// 连接强制停止的槽函数connect(ui->pushButtonT,SIGNAL(clicked()),ct,SLOT(terminate()));// 连接温柔停止的槽函数connect(ui->pushButtonF,SIGNAL(clicked()),this,SLOT(btnFClickedSlot()));ct->start();
}void Dialog::valueSlot(int value)
{// 更新进度条ui->progressBar->setValue(value);
}void Dialog::btnFClickedSlot()
{if(ct != NULL)// 温柔停止ct->isRunning = false;
}

ui界面:

 运行结果:

 

5.5 多线程文件拷贝

子线程不能进行UI操作!!!!!!!!!!

dialog.h

#ifndef DIALOG_H
#define DIALOG_H#include <QtWidgets>
#include <QButtonGroup>
#include <QFileInfo>
#include <QDateTime>
#include "copythread.h"namespace Ui {
class Dialog;
}class Dialog : public QDialog
{Q_OBJECTpublic:explicit Dialog(QWidget *parent = 0);~Dialog();private:Ui::Dialog *ui;QButtonGroup * group;// 获得读取的路径、获得写出的路径void getReadPath();void getWritePath();QString readPath; // 要读取的路径QString writePath; // 要存储的路径void showFileMessage(); // 展示文件信息void copy(); // 拷贝文件private slots:// 按钮组点击的槽函数void btnsClickedSlot(int);// 路径错误的槽函数void pathErrorSlot();// 接收进度值的槽函数void valueSlot(int);
};#endif // DIALOG_H

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::getReadPath()
{// 过滤器QString filter = "所有格式(*.*);;C++(*.h *.cpp)";// 四个参数,见笔记QString path = QFileDialog::getOpenFileName(this,"打开","D:/",filter);if(path=="" && readPath==""){// 引导用户进行正确的处理QMessageBox::warning(this,"提示","请选择有效路径!");return;}readPath = path;// 展示if(path != "")ui->textBrowserOpen->clear();ui->textBrowserOpen->append(readPath);showFileMessage(); // 展示文件信息
}void Dialog::getWritePath()
{// 过滤器QString filter = "所有格式(*.*);;C++(*.h *.cpp)";// 四个参数,见笔记QString path = QFileDialog::getSaveFileName(this,"保存","E:/",filter);if(path=="" && writePath==""){// 引导用户进行正确的处理QMessageBox::warning(this,"提示","请选择有效路径!");return;}writePath = path;// 展示if(path != "")ui->textBrowserSave->clear();ui->textBrowserSave->append(writePath);
}void Dialog::showFileMessage()
{// 创建文件信息类对象QFileInfo info(readPath);// 获得创建时间QString time = info.created().toString("yyyy-MM-dd hh:mm:ss");ui->textBrowserOpen->append(time.prepend("创建时间:"));// 是否可读bool result = info.isReadable();if(result)ui->textBrowserOpen->append("可读");elseui->textBrowserOpen->append("不可读");// 获取文件大小qint64 size = info.size();QString text;text.setNum(size).prepend("文件大小:").append("字节");ui->textBrowserOpen->append(text);
}void Dialog::copy()
{CopyThread* ct = new CopyThread(readPath,writePath,this);// 线程通信的信号槽connect(ct,SIGNAL(pathErrorSignal()),this,SLOT(pathErrorSlot()));connect(ct,SIGNAL(valueSignal(int)),this,SLOT(valueSlot(int)));ct->start();
}void Dialog::btnsClickedSlot(int id)
{if(id == 1){getReadPath();}else if(id == 2){getWritePath();}else if(id == 3){copy();}
}void Dialog::pathErrorSlot()
{QMessageBox::warning(this,"提示","请选择有效的读写路径!");
}void Dialog::valueSlot(int value)
{// 更新进度条ui->progressBar->setValue(value);if(value == 0) // 开始了{// 屏蔽拷贝按钮ui->pushButtonCopy->setEnabled(false);ui->pushButtonCopy->setText("拷贝中");}else if(value == 100) // 结束了{// 回复拷贝按钮ui->pushButtonCopy->setEnabled(true);ui->pushButtonCopy->setText("开始拷贝");QMessageBox::information(this,"通知",writePath.prepend("文件拷贝完成,请查看"));}
}

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 pathErrorSignal(); // 路径有误的信号void valueSignal(int); // 进度值信号public slots:};#endif // COPYTHREAD_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 readFile(readPath);QFile writeFile(writePath);if(!readFile.exists()){emit pathErrorSignal(); // 路径错误信号return;}// 使用只读模式打开数据流readFile.open(QIODevice::ReadOnly);writeFile.open(QIODevice::WriteOnly);qint64 totalSize = readFile.size(); // 文件总大小qint64 hasCopied = 0; // 已经读写的数据量qint64 lastPer = -1; // 上一次进度值QByteArray buffer; // “小推车”while(!readFile.atEnd()) // 循环读取{buffer = readFile.read(1024); // 一次读取1kb// 输出hasCopied += writeFile.write(buffer);// 计算百分比int value = hasCopied*100/totalSize;// 如果本次进度值与上次不同,就发if(value != lastPer){// 发送给主线程百分比emit valueSignal(value);// 这一次的进度值在下一次变为上一次lastPer = value;}}// 收尾writeFile.flush();writeFile.close();readFile.close();
}

可以随意拖动

 


http://www.ppmy.cn/news/66536.html

相关文章

NeRF原理简介及实现

“ NeRF&#xff08;Neural Radiance Fields&#xff09;是一种先进的计算机图形学技术&#xff0c;能够使用深度全连接神经网络优化场景的连续5D函数表示&#xff0c;从而生成高度逼真的3D场景。该技术的优势在于没有使用离散化的网格或体素来表示场景&#xff0c;而是使用一个…

安卓开发 | 将Vue项目打包为app

知识目录 一、写在前面✨二、Hbuilder X准备&#x1f495;2.1 Hbuilder X简介2.2 下载 三、打包&#x1f495;3.1 获取dist目录3.2 新建5app3.3 替换文件3.4 编写manifast.json文件3.5 app云打包 四、总结撒花&#x1f60a; 一、写在前面✨ 大家好&#xff01;我是初心&#xf…

JVM学习随笔04——垃圾回收

目录 1、垃圾回收的对象&#xff08;死亡的对象需要被回收&#xff0c;那么怎么判断一个对象是不是死亡&#xff1f;&#xff09; 2、引用的类型&#xff1a; 3、&#xff08;方法区垃圾回收&#xff09;前面提到怎么判断一个对象是不是死亡&#xff0c;那怎么判断一个类或者…

「企业应用架构」应用架构概述

在信息系统中&#xff0c;应用架构或应用架构是构成企业架构&#xff08;EA&#xff09;支柱的几个架构域之一 应用架构描述了业务中使用的应用程序的行为&#xff0c;重点是它们如何相互之间以及如何与用户交互。它关注的是应用程序消费和生成的数据&#xff0c;而不是它们的内…

PostgreSQL插件说明

说明 PostgreSQL是一个可扩展的关系型数据库&#xff0c;支持插件机制。插件可以增强PostgreSQL的功能&#xff0c;例如添加新的数据类型、查询优化器、存储引擎等等。以上是一些常用的PostgreSQL插件&#xff0c;它们可以帮助开发者扩展PostgreSQL的功能&#xff0c;提高数据…

【Java零基础入门篇】第 ⑤ 期 - 抽象类和接口(一)

博主&#xff1a;命运之光 专栏&#xff1a;JAVA入门 学习目标 1.了解什么是抽象类&#xff0c;什么是接口&#xff1b; 2.掌握抽象类和接口的定义方法&#xff1b; 3.理解接口和抽象类的使用场景&#xff1b; 4.掌握多态的含义和用法&#xff1b; 5.掌握内部类的定义方法和使用…

Java基本数据类型详解及应用示例

Java作为一门强类型语言&#xff0c;基本数据类型是非常重要的概念。Java中基本数据类型分为四种类别&#xff1a;整数类型、浮点数类型、字符类型和布尔类型。其中&#xff0c;每一种数据类型都有着不同的占用字节数和表示范围&#xff0c;合理使用选择不同的数据类型可以提高…

多线程相关高频面试题

一、线程的基础知识 1、线程和进程的区别&#xff1f; 进程是正在运行程序的实例&#xff0c;进程中包含了线程&#xff0c;每个线程执行不同的任务。不同的进程使用不同的内存空间&#xff0c;在当前进程下的所有线程可以共享内存空间。线程更轻量&#xff0c;线程上下文切换…

小牟Andorid下面MD5具体实现的思路总结

Android的开发往往需要一定数目demo 从今起MD5一些加密算法提取物 看看是如何实现的 首先&#xff0c;我们必须明确为什么加密&#xff1f; 1 数据安全处理 2 防止数据窃取 3 有效的避免恶意攻击 4 保证文件完整性 5 优化搜索 作为文件&#xff08;数据&#xff09;索引…

VMware vSphere Replication 8.7 (for vSphere 8.0U1) - 虚拟机复制和数据保护

请访问原文链接&#xff1a;https://sysin.org/blog/vmware-vsphere-replication-8/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.org 新增功能 vSphere Replication 8.7 | 2023 年 4 月 18 日 | 内部版本 21591677 vSphere Re…

4 IK分词器

4 IK分词器 4.1测试分词器 在添加文档时会进行分词&#xff0c;索引中存放的就是一个一个的词&#xff08;term&#xff09;&#xff0c;当你去搜索时就是拿关键字去匹配词&#xff0c;最终 找到词关联的文档。 测试当前索引库使用的分词器&#xff1a; post 发送&#xff…

【2023华为OD笔试必会20题--C语言版】《17 猜字谜》——字符串

本专栏收录了华为OD 2022 Q4和2023Q1笔试题目,100分类别中的出现频率最高(至少出现100次)的20道,每篇文章包括原始题目 和 我亲自编写并在Visual Studio中运行成功的C语言代码。 仅供参考、启发使用,切不可照搬、照抄,查重倒是可以过,但后面的技术面试还是会暴露的。✨✨…

数据结构-查找-线性结构(顺序、折半、分块)查找

目录 一、顺序查找 *查找效率分析 二、折半查找 *查找效率分析 三、分块查找 *查找效率分析 一、顺序查找 有称线性查找&#xff0c; 算法思想&#xff1a;从头到尾挨个查找(反过来也行) typedef struct{int *elem; //数据int TableLen; …

Git 常用命令(优化版本)

Git常用命令 仓库 初始化一个Git代码库&#xff1a;git init克隆一个项目和它的整个代码历史&#xff1a;git clone [url]配置 显示当前的Git配置&#xff1a;git config --list编辑Git配置文件&#xff1a;git config -e [--global]设置提交代码时的用户信息&#xff1a;gi…

java基础知识梳理

虽然已经在实际工作中与java打交道5年之多&#xff0c;但是一直没系统地对java这门语言进行梳理和总结&#xff0c;掌握的知识也比较零散。恰好利用这段时间重新认识下java&#xff0c;并对一些常见的语法和知识点做个总结与回顾&#xff0c;一方面为了加深印象&#xff0c;方便…

浅谈 如何挂载和卸载文件系统,包括使用 mount 和 umount 命令、/etc/fstab 文件等

在Linux系统中&#xff0c;可以使用mount和umount命令来挂载和卸载文件系统。mount命令用于将文件系统挂载到指定的挂载点&#xff0c;umount命令用于卸载已经挂载的文件系统。同时&#xff0c;还可以通过编辑/etc/fstab文件来实现自动挂载文件系统。 使用mount和umount命令 …

SQL 常用函数总结(一)

聚合函数 1. COUNT() COUNT() 函数用于计算一个表格或查询结果集合中的行数。 语法&#xff1a;COUNT(column_name) 或 COUNT(*) column_name 是可选的&#xff0c;表示计算某一列的非空值数量。* 表示计算所有行的数量。 示例1&#xff1a;计算 users 表格中所有记录的数…

【大数据】通过 docker-compose 快速部署 Presto(Trino)保姆级教程

文章目录 一、概述二、前期准备1&#xff09;部署 docker2&#xff09;部署 docker-compose 三、创建网络四、Trino 编排部署1&#xff09;下载 trino2&#xff09;配置1、coordinator 配置2、worker 配置 3&#xff09;启动脚本 bootstrap.sh4&#xff09;构建镜像 Dockerfile…

【Linux】Linux安装tomcat(图文解说详细版)

文章目录 1、安装前置条件2、下载所需压缩包&#xff0c;上传到服务器3、对资源进行解压4、给防火墙添加访问端口&#xff08;默认8080&#xff0c;在它的/conf/server.xml文件里面查看&#xff09;5、然后切换到bin目录下&#xff0c;启动&#xff08;成功效果如图&#xff09…

ASP.NET Core MVC 从入门到精通之文件上传

随着技术的发展&#xff0c;ASP.NET Core MVC也推出了好长时间&#xff0c;经过不断的版本更新迭代&#xff0c;已经越来越完善&#xff0c;本系列文章主要讲解ASP.NET Core MVC开发B/S系统过程中所涉及到的相关内容&#xff0c;适用于初学者&#xff0c;在校毕业生&#xff0c…
最新文章