小工具知识网

当前位置: 首页 > 行业知识 > QT文件IO

行业知识

QT文件IO

2026-10-20 01:19:14 来源:互联网转载

七、文件IO

  1. QFileDialog文件对话框

与QMessageBox一���,QFileDialog也继承了QDialog类,直接使用静态成员函数弹窗,弹窗的结果(选择的文件路径)通过函数返回值返回。

// 获取一个打开或保存的文件路径// 参数1:父对象// 参数2:即windwTitle属性(标题)// 参数3:在那个目录中打开,默认值表示项目的工作目录// 参数4:文件格式过滤器// 返回值:选择的文件路径,如果选择失败,返回空字符QStringgetOpenFileName(        QWidget * parent = 0,         const QString & caption = QString(),         const QString & dir = QString(),         const QString & filter = QString())QString QFileDialog::​getSaveFileName(        QWidget * parent = 0,         const QString & caption = QString(),         const QString & dir = QString(),         const QString & filter = QString())[static]

需要注意以下,QFileDialog只是一个窗口类,本身不具备任何IO的能力。

#include "dialog.h"#include "ui_dialog.h"Dialog::Dialog(QWidget *parent) :    QDialog(parent),    ui(new Ui::Dialog){    ui->setupUi(this);    connect(ui->pushButtonOpen,SIGNAL(clicked()),this,            SLOT(btnClickedSlot()));    connect(ui->pushButtonSave,SIGNAL(clicked()),this,            SLOT(btnClickedSlot()));}Dialog::~Dialog(){    delete ui;}void Dialog::btnClickedSlot(){    if(ui->pushButtonOpen == sender())    {        QString filter = "所有文件(*.*);;Qt(*.cpp *.h *.pro *.ui)";        QString path = QFileDialog::getOpenFileName(this,"打开","D:/",filter);        if(path != "")        {            readPath = path;            ui->textBrowserOpen->append(path);        }        else if(readPath == "")        {            QMessageBox::warning(this,"提示","请选择打开的文件");        }    }    else if(ui->pushButtonSave == sender())    {        QString filter = "所有文件(*.*);;Qt(*.cpp *.h *.pro *.ui)";        QString path = QFileDialog::getSaveFileName(this,"保存","D:/",filter);        if(path != "")        {            writePath = path;            ui->textBrowserSave->append(path);        }        else if(writePath == "")        {            QMessageBox::warning(this,"提示","请选择保存的文件");        }    }    else if(ui->pushButtonCopy == sender())    {    }}

2、QFileInfo文件信息类

只需要创建出对象后,通过各种成员函数直接获取文件信息。

// 构造函数// 参数为文件路径,如果文件非法,仍然可以创建出QFileInfo对象QFileInfo::​QFileInfo(const QString & file)
// 判断文件是否存在// 如果存在则返回true。否则返回falsebool QFileInfo::​exists() const
// 返回基础名称,不包含后缀QString QFileInfo::​baseName() const
// 获取文件大小// 返回为文件大小qint64 QFileInfo::​size() const
// 返回文件可读性,true可读、false不可读bool QFileInfo::​isReadable() const
#include "dialog.h"#include "ui_dialog.h"Dialog::Dialog(QWidget *parent) :    QDialog(parent),    ui(new Ui::Dialog){    ui->setupUi(this);    connect(ui->pushButtonOpen,SIGNAL(clicked()),this,            SLOT(btnClickedSlot()));    connect(ui->pushButtonSave,SIGNAL(clicked()),this,            SLOT(btnClickedSlot()));}Dialog::~Dialog(){    delete ui;}void Dialog::printFileInfo(){    // 创建文件对象    QFileInfo fileInfo(readPath);    // 判断文件是否存在    if(!fileInfo.exists())    {        QMessageBox::warning(this,"提示","文件路径无效");        return;    }    QString text = fileInfo.baseName();    text.prepend("文件名称:");    ui->textBrowserOpen->append(text);    qint64 size = fileInfo.size();    text = QString::number(size);    text.prepend("文件大小:").append("字节");    ui->textBrowserOpen->append(text);    if(fileInfo.isReadable())    {        ui->textBrowserOpen->append("文件可读");    }    else    {        ui->textBrowserOpen->append("文件不可读");    }}void Dialog::btnClickedSlot(){    if(ui->pushButtonOpen == sender())    {        QString filter = "所有文件(*.*);;Qt(*.cpp *.h *.pro *.ui)";        QString path = QFileDialog::getOpenFileName(this,"打开","D:/",filter);        if(path != "")        {            readPath = path;            ui->textBrowserOpen->append(path);            printFileInfo(); // 文件读取成功后输出信息        }        else if(readPath == "")        {            QMessageBox::warning(this,"提示","请选择打开的文件");        }    }    else if(ui->pushButtonSave == sender())    {        QString filter = "所有文件(*.*);;Qt(*.cpp *.h *.pro *.ui)";        QString path = QFileDialog::getSaveFileName(this,"保存","D:/",filter);        if(path != "")        {            writePath = path;            ui->textBrowserSave->append(path);        }        else if(writePath == "")        {            QMessageBox::warning(this,"提示","请选择保存的文件");        }    }    else if(ui->pushButtonCopy == sender())    {    }}

3、QFile文件读写类

在Qt中所有IO类都继承自QIODevice类,QIODevice类中规定了最基础的IO相关接口,这些接口虽然在不同的派生类中可能实现有所不同,但调用方式一致。

// 构造函数// 参数为文件路径,如果是非法路径,也能创建出QFIle对象,但是不能正常IO输入输出操作。QFile::​QFile(const QString & name)
// 判断QFile对应的文件是否存在bool QFile::​exists() const
// 打开数据流// 参数为打开模式、只读模式、只写模式、读写模式bool QIODevice::​open(OpenMode mode)[virtual]
// 构造函数,QByteArray是qt中的常用数组// 构造一个空子节数组QByteArray::​QByteArray()
// 是否读取到文件末尾bool QIODevice::​atEnd() const[virtual]
// 读取数据// 参数:每次读取的大小// 返回值QByteArray字符数组QByteArray QIODevice::​read(qint64 maxSize)
// 写入数据// 要写入的数据// 返回值,本次写入的大小,返回值为-1表示写入失败qint64 QIODevice::​write(const QByteArray & byteArray)
// 清空缓冲区bool QFileDevice::​flush()
// 关闭数据流void QIODevice::​close()[virtual]

dialog.cpp

#include "dialog.h"#include "ui_dialog.h"Dialog::Dialog(QWidget *parent) :    QDialog(parent),    ui(new Ui::Dialog){    ui->setupUi(this);    connect(ui->pushButtonOpen,SIGNAL(clicked()),this,            SLOT(btnClickedSlot()));    connect(ui->pushButtonSave,SIGNAL(clicked()),this,            SLOT(btnClickedSlot()));    connect(ui->pushButtonCopy,SIGNAL(clicked()),this,            SLOT(btnClickedSlot()));}Dialog::~Dialog(){    delete ui;}void Dialog::printFileInfo(){    // 创建文件对象    QFileInfo fileInfo(readPath);    // 判断文件是否存在    if(!fileInfo.exists())    {        QMessageBox::warning(this,"提示","文件路径无效");        return;    }    QString text = fileInfo.baseName();    text.prepend("文件名称:");    ui->textBrowserOpen->append(text);    qint64 size = fileInfo.size();    text = QString::number(size);    text.prepend("文件大小:").append("字节");    ui->textBrowserOpen->append(text);    if(fileInfo.isReadable())    {        ui->textBrowserOpen->append("文件可读");    }    else    {        ui->textBrowserOpen->append("文件不可读");    }}void Dialog::copy(){    if(readPath == "")    {        QMessageBox::warning(this,"提示","请选择要读取的文件");        return;    }    if(writePath == "")    {        QMessageBox::warning(this,"提示","请选择要写入的文件");        return;    }    // 正在拷贝时屏蔽拷贝按钮    ui->pushButtonCopy->setEnabled(false);    // 创建QFile对象    QFile readFile(readPath);    QFile writeFile(writePath);    // 打开文件流    readFile.open(QIODevice::ReadOnly); // 只读模式    writeFile.open(QIODevice::WriteOnly); // 只写模式    // 添加进度条进度    qint64 totalSize = readFile.size(); // 获取文件总大小    qint64 hasRead = 0; // 已经读写的大小    QByteArray array;    while(!readFile.atEnd())    {        array = readFile.read(1024); // 每次读取1kb        qint64 writeRet = writeFile.write(array); // 写入数据,返回值为本次写入的大小        if(writeRet == -1) // 如果写入数据的返回值为-1表示写入失败        {            QMessageBox::critical(this,"错误","文件拷贝失败");            return;        }        // 将写出的数据设置给进度条        hasRead += writeRet; // 获取已经写入总大小        int per = hasRead *100/totalSize; // 计算百分比        ui->progressBar->setValue(per); // 设置给进度条    }    // 清空缓存区    writeFile.flush();    // 关闭数据流    readFile.close();    writeFile.close();    // 拷贝完成后解除按钮屏蔽    ui->pushButtonCopy->setEnabled(true);    // 添加拷贝完成提示框    QMessageBox::information(this,"提示","拷贝完成");}void Dialog::btnClickedSlot(){    if(ui->pushButtonOpen == sender())    {        QString filter = "所有文件(*.*);;Qt(*.cpp *.h *.pro *.ui)";        QString path = QFileDialog::getOpenFileName(this,"打开","D:/",filter);        if(path != "")        {            readPath = path;            ui->textBrowserOpen->append(path);            printFileInfo(); // 文件读取成功后输出信息        }        else if(readPath == "")        {            QMessageBox::warning(this,"提示","请选择打开的文件");        }    }    else if(ui->pushButtonSave == sender())    {        QString filter = "所有文件(*.*);;Qt(*.cpp *.h *.pro *.ui)";        QString path = QFileDialog::getSaveFileName(this,"保存","D:/",filter);        if(path != "")        {            writePath = path;            ui->textBrowserSave->append(path);        }        else if(writePath == "")        {            QMessageBox::warning(this,"提示","请选择保存的文件");        }    }    else if(ui->pushButtonCopy == sender())    {        copy();    }}

[思考]上面的代码有没有问题?

当拷贝大文件时,会出现卡顿,如果尝试关闭,则触发:

4、UI与耗时操作

在默认情况下,Qt的项目是单线程,这个自带的线程用于处理程序的主要任务和UI交互也被称为主线程或UI线程。

如果在主线程中执行耗时操作(IO或复杂算法)会导致主线程原本执行的操作被阻塞,甚至无法关闭,形成”假***“的现象。

当操作系统发现某个进程无法被正常关闭时,会弹出程序未响应窗口引导用户选择是否强制关闭当前进程。

解决方法是使用多线程。

5、QThread线程类

5.1 复现程序未响应

QThread类是Qt的线程类,

// 强制线程休眠msecs个毫秒voidmsleep(unsigned long msecs)
#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->pushButtonClose,SIGNAL(clicked()),            this,SLOT(close()));}Dialog::~Dialog(){    delete ui;}void Dialog::btnSleepClickedSlot(){    qDebug()     ui-setupUi(this);    connect(ui-pushButtonSleep,SIGNAL(clicked()),            this,SLOT(btnSleepClickedSlot()));    connect(ui-pushButtonClose,SIGNAL(clicked()),            this,SLOT(close()));}Dialog::~Dialog(){    delete ui;}void Dialog::btnSleepClickedSlot(){     // 创建子线程对象     MyThread *mt = new MyThread(this);     // 启动子线程     mt-start();}

5.3 异步刷新

在实际的开发中,两个线程不可能毫无关系的前提下各干各的,最常见的情况是主线程分配一个耗时任务给子线程,子线程需要把耗时任务的执行情况反馈给主线程。主线程刷新子线程耗时的操作,并展示对应的UI效果。

【例子】:子线程执行文件拷贝,主线程显示拷贝的进度。

通常子线程是主线程对象的子对象,因此异步刷新就是对象通信的问题。使用信号槽解决。

咱们写一个简单的例子,一个伪拷贝的案例,使用for循环加睡眠, 模拟文件拷贝的功能。

今晚作业:真正的子线程拷贝文件。

#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(btnSleepClickedSlot()));}Dialog::~Dialog(){    delete ui;}void Dialog::btnSleepClickedSlot(){    ui->pushButton->setEnabled(false);     // 创建子线程对象     MyThread *mt = new MyThread(this);     connect(mt,SIGNAL(valueSignal(int)),this,             SLOT(valueSlot(int)));     // 启动子线程     mt->start();}void Dialog::valueSlot(int value){    ui->progressBar->setValue(value);    if(value == 100)    {        ui->pushButton->setEnabled(true);        QMessageBox::warning(this,"提示","拷贝完成");    }}

问题:频繁抖动窗口,会出现焦点抢夺问题,导致卡***。使用hide函数,来隐藏主窗口。

5.4 线程停止

子线程往往执行耗时操作,耗时操作又往往伴随着循环,因此并不建议使用粗暴的方式直接停止线程,因为强行停止线程会导致耗时操作资源无法回收等问题。

可以通过给循环设置标志位的方式使线程停止。

#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(btnSleepClickedSlot()));}Dialog::~Dialog(){    delete ui;}void Dialog::btnSleepClickedSlot(){    if(ui->pushButton->text() == "开始拷贝")    {        // 创建子线程对象        mt = new MyThread(this);        connect(mt,SIGNAL(valueSignal(int)),this,                SLOT(valueSlot(int)));        // 启动子线程        mt->start();        ui->pushButton->setText("停止拷贝");    }    else if(ui->pushButton->text() == "停止拷贝")    {        ui->pushButton->setText("开始拷贝");        mt->setRunningState(false);    }}void Dialog::valueSlot(int value){    ui->progressBar->setValue(value);    if(value == 100)    {        this->hide(); // 隐藏主窗口        this->show(); // 显示主窗口        QMessageBox::warning(this,"提示","拷贝完成");    }}

上一篇:为什么创新?

下一篇:戴尔Win10重装系统详细教程,一步步轻松搞定!