QT文件IO

七、文件IO

  1. QFileDialog文件对话框

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

// 获取一个打开或保存的文件路径
// 参数1:父对象
// 参数2:即windwTitle属性(标题)
// 参数3:在那个目录中打开,默认值表示项目的工作目录
// 参数4:文件格式过滤器
// 返回值:选择的文件路径,如果选择失败,返回空字符
QString	getOpenFileName(
        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。否则返回false
bool 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个毫秒
void	msleep(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() << "开始睡觉" ;
    QThread::msleep(15000);
    qDebug() << "睡眠结束" ;
}

5.2 创建并启动一个子线程

主线程以外的线程都是子线程,子线程不能执行主线程的UI操作。只能执行耗时操作。

下面是创建并启动一个自定义子线程的步骤:

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

  1. 在弹出的窗口中,先设置类名,然后在选择基类名称QObject,最后点击”下一步“。

  1. 在项目管理界面,直接点击完成,可以看到线程类的文件已经创建。

  1. 选择新建的头文件,把继承的QObject更改为QThread

  1. 选择新建的.Cpp文件,把透传构造的QObject更改为QThread

  1. 在自定义线程中,覆盖基类QThread的run函数。

// 此函数是子线程执行的起始点,也是子线程的结束点。
void QThread::​run()[vritual protected]

7、在run函数的函数体中编写子线程要执行的耗时操作。

  1. 在主线程中创建子线程并启动

// 启动子线程,调用此函数后,会在子线程中自动执行run函数
// 参数欸子线程执行的优先级,默认值为创建所在的线程相同优先级
void QThread::​start(Priority priority = InheritPriority)[slot]

#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()
{
     // 创建子线程对象
     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,"提示","拷贝完成");}
}

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

如何让电脑待机而wifi不关的操作方法!!

1、一台电脑如果一天不关机&#xff0c;大约消耗0.3度电。 一般一台电脑的功耗约为250-400W&#xff08;台式机&#xff09;。 一台电脑每月的耗电量&#xff1a;如果是每小时300W每天10小时每月30天90KW&#xff0c;即90千瓦时的电。 这只是保守估计。 2、使用完毕后正常关闭…

蓝桥杯-最小砝码

知识点&#xff1a;本题主要考察任何一个物体都可以用 3进制表示。 #include <iostream> #include<cmath> using namespace std; //知识点:任何一个物体都可以用 3进制表示 int main() { int n; cin >> n; int sum 0; for (int i 0;; i)…

Linux-实用操作(黑马学习笔记)

各类小技巧&#xff08;快捷键&#xff09; ctrl c 强制停止 ● Linux某些程序的运行&#xff0c;如果想要强制停止它&#xff0c;可以使用快捷键ctrl c ● 命令输入错误&#xff0c;也可以通过快捷键ctrl c&#xff0c;退出当前输入&#xff0c;重新输入 ctrl d 退出或登…

在autodl搭建stable-diffusion-webui+sadTalker

本文介绍在autodl.com搭建gpu服务器&#xff0c;实现stable-diffusion-webuisadTalker功能&#xff0c;图片音频 可生成视频。 autodl租GPU 自己本地部署SD环境会遇到各种问题&#xff0c;网络问题&#xff08;比如huggingface是无法访问&#xff09;&#xff0c;所以最好的方…

红队攻防之powershell上线基础免杀(一)

不努力&#xff0c;你背井离乡干嘛&#xff1f;当卧底啊 环境为win10&#xff0c;在哥斯拉生成的webshell下&#xff0c;执行powershell命令。 测试杀毒软件为&#xff1a;火绒&#xff0c;腾讯电脑管家 哥斯拉生成php文件的webshell 如图 哥斯拉进行连接 把要执行命令的文件…

[嵌入式系统-35]:RT-Thread -20- 新手指南:在Keil MDK-ARM 模拟器上运行RT-Thread

目录 前言&#xff1a; 一、Keil MDK-ARM 模拟器概述 1.1 Keil概述 1.2 Keil MDK-ARM 1.3 Keil MDK-ARM软件仿真模拟器 1.4 Keil模拟器支持的CPU类型 二、Keil MDK ARM安装 前言&#xff1a; 一般嵌入式操作系统因为它的特殊性&#xff0c;往往和硬件平台密切相关连&am…

LeetCode_Java_环形链表(题目+思路+代码)

141.环形链表 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整数 pos 来表示链表尾连接到链表中的位…

再探二分法

推荐阅读 智能化校园&#xff1a;深入探讨云端管理系统设计与实现&#xff08;一&#xff09; 智能化校园&#xff1a;深入探讨云端管理系统设计与实现&#xff08;二&#xff09; 文章目录 推荐阅读二分查找题目思路解法左闭右闭式写法左闭右开式写法 二分查找 题目 给定一个…

Kafka之Producer源码

Producer源码解读 在 Kafka 中, 我们把产生消息的一方称为 Producer 即 生产者, 它是 Kafka 的核心组件之一, 也是消息的来源所在。它的主要功能是将客户端的请求打包封装发送到 kafka 集群的某个 Topic 的某个分区上。那么这些生产者产生的消息是怎么传到 Kafka 服务端的呢&a…

免编程经验,搭建宠物店小程序轻松实现

在如今的互联网时代&#xff0c;小程序商城已成为各行业推广和销售的热门方式。对于花店来说&#xff0c;搭建一个自己的小程序商城不仅可以提升品牌形象&#xff0c;还可以方便顾客在线选购花卉产品。下面就来教大家如何轻松搭建一个花店小程序商城&#xff0c;并通过引流获得…

信息安全计划

任何管理人员或人力资源专业人士都知道&#xff0c;除非彻底记录标准和实践&#xff0c;否则永远无法真正实施和执行标准和实践。正如您可能想象的那样&#xff0c;在保护您的网络、技术和数据系统免受网络威胁以及在发生这些事件时规划最及时、高效和有效的响应时&#xff0c;…

【C++】C++对C语言的关系,拓展及命名空间的使用

文章目录 &#x1f4dd;C简述C融合了3种不同的编程方式&#xff1a;C和C语言关系是啥呢&#xff1f;C标准 &#x1f320;C应用&#x1f320;C语言优点第一个C程序 &#x1f320;命名空间&#x1f320;命名空间的使用命名空间的定义 &#x1f320;怎么使用命名空间中的内容呢&am…

Centos中安装Docker及Docker的使用

在centos7系统中安装指定版本的docker,并通过docker使用安装mysql为例,阐述docker的使用。 2.1、Docker卸载及安装yum依赖 【卸载Docker,如果安装的Docker的版本不合适】 yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-…

机器学习:SVM算法(Python)

一、核函数 kernel_func.py import numpy as npdef linear():"""线性核函数:return:"""def _linear(x_i, x_j):return np.dot(x_i, x_j)return _lineardef poly(degree3, coef01.0):"""多项式核函数:param degree: 阶次:param …

【Java程序设计】【C00317】基于Springboot的智慧社区居家养老健康管理系统(有论文)

基于Springboot的智慧社区居家养老健康管理系统&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于Springboot的智慧社区居家养老健康管理系统设计与实现&#xff0c;本系统有管理员、社区工作人员、医生以及家属四种角色权限 管…

Vue + Echarts页面内存占用高问题解决

Vue Echarts页面内存占用高问题解决 1.问题描述 目前使用的是Vue2 Echarts4.x的组合&#xff0c;页面如下所示。 就是一个类似于神策的数据看板页面&#xff0c;左侧是一个导航栏&#xff0c;右侧看板页面中包含很多个报表图片&#xff0c;其中报表页面中对Echarts图表进…

微服务基础环境搭建

一.创建父工程 用于聚合其他微服务模块 1 新建 Maven 项目 JDK8Maven 项目Web 2 项目设置 编码的选择 UTF8JDK 版本的选择 3 删除 src 目录 4 配置父级 pom.xml SpringBoot&#xff1a;模块探究之spring-boot-dependencies-CSDN博客 子模块能够依赖当前父级 pom.xml 配置 【My…

二阶低通滤波器(博途PLC SCL源代码)

在学习滤波器之前我们先了解下截止频率的准确定义,周期正弦信号经过传递函数后的输出信号,其幅值衰减-3dB时对应的频率。-3dB的含义是幅值衰减为原来的约0.707。更多滤波器信号处理相关内容请参看下面文章链接: 1、PLC一阶低通滤波器 https://rxxw-control.blog.csdn.net/…

ESP32-FreeRtos任务-1

头文件 task. h 函数说明 创建任务 BaseType_t xTaskCreate( TaskFunction_t pvTaskCode,const char * const pcName,const configSTACK_DEPTH_TYPE uxStackDepth,void *pvParameters,UBaseType_t uxPriority,TaskHandle_t *pxCreatedTask); 创建一个新任务并将其添加到…

申请攻读博士学位研究生相关模板资料(包括专家推荐信、学术简历、研究计划及范文、回复导师邮件)

申请攻读博士学位研究生相关模板资料&#xff08;包括专家推荐信、学术简历、研究计划及范文、回复导师邮件&#xff09; 博士是对攻读博士学位的研究生的称呼&#xff0c;同样也可用来称呼已获得博士学位的人员。 主要通过拥有博士点的普通高等学校和拥有博士研究生培养资格…