嵌入式学习-QT-Day07

嵌入式学习-QT-Day07

七、文件IO

1、QFileDialog文件对话框

2、QFileInfo文件信息类

3、QFile文件读写类(重点)

4、UI与耗时操作

5、QThread线程类

[5.1 复现程序未响应](#5.1 复现程序未响应)

[5.2 创建并启动一个子线程](#5.2 创建并启动一个子线程)

[5.3 异步刷新](#5.3 异步刷新)

[5.4 线程停止](#5.4 线程停止)

6、数据持久化

七、文件IO

1、QFileDialog文件对话框

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

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

`QString QFileDialog::​getSaveFileName(`
`    QWidget * parent =` `0,` 
    `const QString & caption =` `QString(),` 
    `const QString & dir =` `QString(),` 
    `const QString & filter =` `QString(),` 
`)[static]`
`

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

dialog.h

复制代码
#ifndef DIALOG_H`
`#define DIALOG_H`

`#include <QDialog>`
`#include <QFileDialog>`
`#include <QMessageBox>`

`namespace Ui {`
`class Dialog;`
`}`

`class Dialog : public QDialog`
`{`
`    Q_OBJECT`

`public:`
`    explicit Dialog(QWidget *parent = 0);`
`    ~Dialog();`

`private:`
`    Ui::Dialog *ui;`
`    QString readPath;`
`    QString writePath;`

`private slots:`
`    void btnClickedSlot();`
`};`

`#endif // DIALOG_H`
`

dilaog.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()));`

`}`

`Dialog::~Dialog()`
`{`
`    delete ui;`
`}`

`void Dialog::btnClickedSlot()`
`{`
`    if(ui->pushButtonOpen == sender())`
`    {`
`        QString filter = "所有文件(*.*);;Qt(*.cpp *.h *.ui *.pro)";`
`        QString path = QFileDialog::getOpenFileName(this,"打开","D:/",filter);`
`        if(path != "")`
`        {`
`            ui->textBrowserOpen->append(path);`
`            readPath = path;`
`        }`
`        else if(readPath == "")`
`        {`
`            // 弹窗`
`            QMessageBox::warning(this,"警告","请选择要打开的文件");`
`        }`

`    }`
`    else if(ui->pushButtonSave == sender())`
`    {`
`        QString filter = "所有文件(*.*);;Qt(*.cpp *.h *.ui *.pro)";`
`        QString path = QFileDialog::getSaveFileName(this,"保存","D:/",filter);`
`        if(path != "")`
`        {`
`            ui->textBrowserSave->append(path);`
`            writePath = path;`
`        }`
`        else if(writePath == "")`
`        {`
`            // 弹窗`
`            QMessageBox::warning(this,"警告","请选择要保存的文件");`
`        }`
`    }`
`    else`
`    {`

`    }`
`}`
`

2、QFileInfo文件信息类

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

复制代码
// 构造函数`
`// 参数为文件路径,如果文件非法,仍然可以创建出QFileInfo对象`
`QFileInfo::​QFileInfo(const QString & file)
复制代码
// 判断文件是否存在`
`// 如果存在返回ture,不存在返回false`
`bool QFileInfo::​exists() const
复制代码
// 返回基础文件名,不包含后缀`
`QString QFileInfo::​baseName() const
复制代码
// 返回文件可读性,true可读,false不可读`
`bool QFileInfo::​isReadable() const

dialog.h

复制代码
#ifndef DIALOG_H`
`#define DIALOG_H`

`#include <QDialog>`
`#include <QFileDialog>`
`#include <QMessageBox>`
`#include <QFileInfo>`

`namespace Ui {`
`class Dialog;`
`}`

`class Dialog : public QDialog`
`{`
`    Q_OBJECT`

`public:`
`    explicit Dialog(QWidget *parent = 0);`
`    ~Dialog();`
`    void printFileInfo();`

`private:`
`    Ui::Dialog *ui;`
`    QString readPath;`
`    QString writePath;`

`private slots:`
`    void btnClickedSlot();`
`};`

`#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->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())`
`    {`
`        return;`
`    }`

`    // 获取文件大小`
`    qint64 fileSize = fileInfo.size();`
`    QString text = QString::number(fileSize);`
`    text.prepend("文件大小:").append("字节");`
`    ui->textBrowserOpen->append(text);`

`    // 获取文件名称`
`    text = fileInfo.baseName();`
`    text.prepend("文件名称:");`
`    ui->textBrowserOpen->append(text);`


`    // 获取文件可读性`
`    bool result = fileInfo.isReadable();`
`    if(result)`
`    {`
`        ui->textBrowserOpen->append("文件可读!!");`
`    }`
`    else`
`    {`
`        ui->textBrowserOpen->append("文件不可读!!");`
`    }`
`}`



`void Dialog::btnClickedSlot()`
`{`
`    if(ui->pushButtonOpen == sender())`
`    {`
`        QString filter = "所有文件(*.*);;Qt(*.cpp *.h *.ui *.pro)";`
`        QString path = QFileDialog::getOpenFileName(this,"打开","D:/",filter);`
`        if(path != "")`
`        {`
`            ui->textBrowserOpen->append(path);`
`            readPath = path;`
`            printFileInfo();`
`        }`
`        else if(readPath == "")`
`        {`
`            // 弹窗`
`            QMessageBox::warning(this,"警告","请选择要打开的文件");`
`        }`

`    }`
`    else if(ui->pushButtonSave == sender())`
`    {`
`        QString filter = "所有文件(*.*);;Qt(*.cpp *.h *.ui *.pro)";`
`        QString path = QFileDialog::getSaveFileName(this,"保存","D:/",filter);`
`        if(path != "")`
`        {`
`            ui->textBrowserSave->append(path);`
`            writePath = path;`
`        }`
`        else if(writePath == "")`
`        {`
`            // 弹窗`
`            QMessageBox::warning(this,"警告","请选择要保存的文件");`
`        }`
`    }`
`    else`
`    {`

`    }`
`}`
`

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]
复制代码
// 是否读到文件尾部`
`bool QIODevice::​atEnd() const
复制代码
// 读取最大长度为maxSize个的字节到返回值中`
`QByteArray QIODevice::​read(qint64 maxSize)
复制代码
// 构造函数,QByteArray是Qt中常用的数组`
`// 构造一个空字节数组`
`QByteArray::​QByteArray()
复制代码
// 写出数据`
`// 参数为写出的内容`
`// 返回值为实际的数据写出的字节数,出错返回-1`
`qint64 QIODevice::​write(const QByteArray & byteArray)

dialog.h

复制代码
#ifndef DIALOG_H`
`#define DIALOG_H`

`#include <QDialog>`
`#include <QFileDialog>`
`#include <QMessageBox>`
`#include <QFileInfo>`
`#include <QFile>`

`namespace Ui {`
`class Dialog;`
`}`

`class Dialog : public QDialog`
`{`
`    Q_OBJECT`

`public:`
`    explicit Dialog(QWidget *parent = 0);`
`    ~Dialog();`
`    void printFileInfo();`
`    void copy();`

`private:`
`    Ui::Dialog *ui;`
`    QString readPath;`
`    QString writePath;`

`private slots:`
`    void btnClickedSlot();`
`};`

`#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->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())`
`    {`
`        return;`
`    }`

`    // 获取文件大小`
`    qint64 fileSize = fileInfo.size();`
`    QString text = QString::number(fileSize);`
`    text.prepend("文件大小:").append("字节");`
`    ui->textBrowserOpen->append(text);`

`    // 获取文件名称`
`    text = fileInfo.baseName();`
`    text.prepend("文件名称:");`
`    ui->textBrowserOpen->append(text);`


`    // 获取文件可读性`
`    bool result = fileInfo.isReadable();`
`    if(result)`
`    {`
`        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(2048);    // 每次读取2kb`
`        qint64 writeRet = writeFile.write(array); // 写出数据,返回值为本次写出的大小`
`        if(writeRet == -1)  // 如果写出函数的返回值为-1,表示写出失败`
`        {`
`            QMessageBox::critical(this,"错误","文件拷贝失败");`

`            // 清空缓冲区`
`            writeFile.flush();`

`            // 关闭数据流`
`            readFile.close();`
`            writeFile.close();`
`            // 拷贝失败时,解除按钮的屏蔽效果`
`            ui->pushButtonCopy->setEnabled(true);`
`            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 *.ui *.pro)";`
`        QString path = QFileDialog::getOpenFileName(this,"打开","D:/",filter);`
`        if(path != "")`
`        {`
`            ui->textBrowserOpen->append(path);`
`            readPath = path;`
`            printFileInfo();`
`        }`
`        else if(readPath == "")`
`        {`
`            // 弹窗`
`            QMessageBox::warning(this,"警告","请选择要打开的文件");`
`        }`

`    }`
`    else if(ui->pushButtonSave == sender())`
`    {`
`        QString filter = "所有文件(*.*);;Qt(*.cpp *.h *.ui *.pro)";`
`        QString path = QFileDialog::getSaveFileName(this,"保存","D:/",filter);`
`        if(path != "")`
`        {`
`            ui->textBrowserSave->append(path);`
`            writePath = path;`
`        }`
`        else if(writePath == "")`
`        {`
`            // 弹窗`
`            QMessageBox::warning(this,"警告","请选择要保存的文件");`
`        }`
`    }`
`    else if(ui->pushButtonCopy == sender())`
`    {`
`        copy();`
`    }`
`    else`
`    {`

`    }`
`}

【思考】上面的代码有没有问题?

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

4、UI与耗时操作

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

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

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

解决方式是使用多线程。

5、QThread线程类

5.1 复现程序未响应

QThread类是Qt的线程类,可以使用下面的函数模拟耗时操作。

复制代码
// 强制线程休眠msecs个毫秒`
`void QThread::​msleep(unsigned long msecs)[static]`
`

dialog.h

复制代码
#ifndef DIALOG_H`
`#define DIALOG_H`

`#include <QDialog>`
`#include <QThread>`
`#include <QDebug>`

`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();`
`};`

`#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->pushButtonSleep,SIGNAL(clicked()),`
`            this,SLOT(btnSleepClickedSlot()));`

`    connect(ui->pushButtonClose,SIGNAL(clicked()),`
`            this,SLOT(close()));`
`}`

`Dialog::~Dialog()`
`{`
`    delete ui;`
`}`

`void Dialog::btnSleepClickedSlot()`
`{`
`    qDebug() << "睡眠开始";`
`    QThread::msleep(10000);`
`    qDebug() << "睡眠结束";`
`}`
`

5.2 创建并启动一个子线程

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

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

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

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

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

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

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

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

复制代码
// 此函数是子线程的执行的起始点,也是子线程的结束点。`
`void QThread::​run()[virtual]
  1. 在run函数的函数体中编写子线程要执行的耗时操作
  2. 创建自定义子线程对象,并调用start函数启动子线程。
复制代码
// 启动子线程,启动后会执行run`
`// 参数:线程调度优先级,默认是与父线程相同优先级`
`void QThread::​start(Priority priority = InheritPriority)

dialog.h

复制代码
#ifndef DIALOG_H`
`#define DIALOG_H`

`#include <QDialog>`
`#include <QThread>`
`#include <QDebug>`
`#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();`
`};`

`#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->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();`
`}`
`

mythread.h

复制代码
#ifndef MYTHREAD_H`
`#define MYTHREAD_H`

`#include <QThread>`
`#include <QDebug>`

`class MyThread : public QThread`
`{`
`    Q_OBJECT`
`public:`
`    explicit MyThread(QObject *parent = 0);`
`    ~MyThread();`

`    void run();`
`signals:`

`public slots:`
`};`

`#endif // MYTHREAD_H`
`

mythread.cpp

复制代码
#include "mythread.h"`

`MyThread::MyThread(QObject *parent) : QThread(parent)`
`{`

`}`

`MyThread::~MyThread()`
`{`

`}`

`void MyThread::run()`
`{`
`    qDebug() << "睡眠开始";`
`    QThread::msleep(10000);`
`    qDebug() << "睡眠结束";`
`}`
`

5.3 异步刷新

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

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

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

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

今天晚上的作业:实现真正的拷贝功能。

Dialog.h

复制代码
#ifndef DIALOG_H`
`#define DIALOG_H`

`#include <QDialog>`
`#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 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()`
`{`
`    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::information(this,"提示","拷贝完成!!");`
`    }`
`}`
`

mythread.h

复制代码
#ifndef MYTHREAD_H`
`#define MYTHREAD_H`

`#include <QThread>`

`class MyThread : public QThread`
`{`
`    Q_OBJECT`
`public:`
`    explicit MyThread(QObject *parent = 0);`
`    ~MyThread();`

`    void run();`

`signals:`
`    void valueSignal(int);`

`public slots:`
`};`

`#endif // MYTHREAD_H`
`

myThread.cpp

复制代码
#include "mythread.h"`

`MyThread::MyThread(QObject *parent) : QThread(parent)`
`{`

`}`

`MyThread::~MyThread()`
`{`

`}`

`void MyThread::run()`
`{`
`    for(int i = 0; i <= 100; i++)`
`    {`
`        QThread::msleep(100);`
`        emit valueSignal(i);`
`    }`
`}`
`

进度条到100时,输出提示框。(问题:频繁抖动窗口,会出现焦点抢夺的问题,导致卡死,解决使用hide函数,来隐藏主窗口)

5.4 线程停止

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

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

dialog.h

复制代码
#ifndef DIALOG_H`
`#define DIALOG_H`

`#include <QDialog>`
`#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 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()`
`{`
`    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::information(this,"提示","拷贝完成!!");`
`    }`
`}`
`

myThread.h

复制代码
#ifndef MYTHREAD_H`
`#define MYTHREAD_H`

`#include <QThread>`
`#include <QDebug>`

`class MyThread : public QThread`
`{`
`    Q_OBJECT`
`public:`
`    explicit MyThread(QObject *parent = 0);`
`    ~MyThread();`

`    void run();`

`    bool getRunningState() const;`
`    void setRunningState(bool value);`

`private:`
`    bool runningState;  // 状态标志`

`signals:`
`    void valueSignal(int);`

`public slots:`
`};`

`#endif // MYTHREAD_H`
`

myThread.cpp

复制代码
#include "mythread.h"`

`MyThread::MyThread(QObject *parent) : QThread(parent)`
`{`
`    runningState = true;`
`}`

`MyThread::~MyThread()`
`{`

`}`

`void MyThread::run()`
`{`
`    for(int i = 0; i <= 100 && runningState; i++)`
`    {`
`        QThread::msleep(100);`
`        emit valueSignal(i);`
`    }`

`    qDebug() << "资源释放成功";`
`}`


`bool MyThread::getRunningState() const`
`{`
`    return runningState;`
`}`

`void MyThread::setRunningState(bool value)`
`{`
`    runningState = value;`
`}`
`

6、数据持久化

数据持久化:是将内存中的数据模型转换为存储模型,以及将存储模型转换为内存中的数据模型的统称。

之前说过的数据库就是一种数据持久化的方式,虽然嵌入式使用的SQLite数据库已经是轻量级数据库了,但是相对于其他技术,还是略显冗余。

Qt中提供比数据库更轻巧的数据持久化的方式------QSettings

复制代码
// 构造函数`
`// 参数1:存储文件的名称,默认为构建目录`
`// 参数2:存储格式(.ini)`
`// 参数3:父对象`
`QSettings::​QSettings(const QString & fileName, Format format, QObject * parent = 0)
复制代码
// 设置INI文件的编码,建议使用UTF-8`
`// 编码字符串`
`void QSettings::​setIniCodec(const char * codecName)
复制代码
// 开始存储,同类型建议使用此函数,以数组的方式进行存储`
`// 参数为数组的名称`
`void QSettings::​beginWriteArray(const QString & prefix, int size = -1)
复制代码
// 以 "组"的方式进行存储,不同类型建议使用此函数(相同类型也可以,但是性能不如上面好)`
`// 参数:"组"的名称`
`void QSettings::​beginGroup(const QString & prefix)
复制代码
// 在"组"中添加键值对(组和数组都用这个函数)`
`// 参数1:键`
`// 参数2:值`
`void QSettings::​setValue(const QString & key, const QVariant & value)
复制代码
// 结束数组的存储`
`void QSettings::​endArray()`
`// 结束组的存储`
`void QSettings::​endGroup()
复制代码
// 根据键获取值`
`// 参数1:键`
`// 参数2:如果取出失败的默认值`
`// 返回值:取出的值`
`QVariant QSettings::​value(const QString & key, const QVariant & defaultValue = QVariant()) const

dialog.h

复制代码
#ifndef DIALOG_H`
`#define DIALOG_H`

`#include <QDialog>`
`#include <QSettings>`

`namespace Ui {`
`class Dialog;`
`}`

`class Dialog : public QDialog`
`{`
`    Q_OBJECT`

`public:`
`    explicit Dialog(QWidget *parent = 0);`
`    ~Dialog();`

`private:`
`    Ui::Dialog *ui;`

`    void load();`

`private slots:`
`    void save();`
`};`

`#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);`

`    load();`
`    connect(ui->pushButton,SIGNAL(clicked()),`
`            this,SLOT(save()));`
`}`

`Dialog::~Dialog()`
`{`
`    delete ui;`
`}`

`// 读取`
`void Dialog::load()`
`{`
`    // 创建一个临时使用的QSettings对象`
`    QSettings setting("config.ini",QSettings::IniFormat);`

`    // 设置文件编码UTF-8`
`    setting.setIniCodec("UTF-8");`

`    // 开始以组的方式读取数据`
`    setting.beginGroup("24081");`

`    // 开始读数据`
`    QString name = setting.value("name").toString();`
`    ui->lineEdit->setText(name);`
`    QString text = setting.value("gender").toString();`
`    ui->comboBox->setCurrentText(text);`
`    int age = setting.value("age").toInt();`
`    ui->spinBox->setValue(age);`

`    // 结束组`
`    setting.endGroup();`
`}`

`// 存储`
`void Dialog::save()`
`{`
`    // 创建一个临时使用的QSettings对象`
`    QSettings setting("config.ini",QSettings::IniFormat);`

`    // 设置文件编码 UTF-8`
`    setting.setIniCodec("UTF-8");`

`    // 开始以组的方式存储数据`
`    setting.beginGroup("24081");`

`    // 开始写数据`
`    setting.setValue("name",ui->lineEdit->text());`
`    setting.setValue("gender",ui->comboBox->currentText());`
`    setting.setValue("age",ui->spinBox->value());`

`    // 结束组`
`    setting.endGroup();`
`}
相关推荐
小杨爱学习zb1 分钟前
学习总结 网格划分+瞬态求解设置
笔记·学习·算法
Zfox_2 分钟前
【C++项目】从零实现RPC框架「四」:业务层实现与项目使用
linux·开发语言·c++·rpc·项目
我想吃余5 分钟前
【C++篇】类与对象(上篇):从面向过程到面向对象的跨越
开发语言·c++
強云17 分钟前
界面架构 - MVVM (Qt)
qt·架构
想睡hhh17 分钟前
c++概念——入门基础概念
开发语言·c++
互联网上的猪26 分钟前
Excel时间类型函数(包括today、date、eomonth、year、month、day、weekday、weeknum、datedif)
笔记·学习·excel
愚润求学31 分钟前
【C++】vector的模拟实现
开发语言·c++·stl·语法
又过一个秋39 分钟前
【sylar-webserver】7 定时器模块
linux·c++
weixin_5354557943 分钟前
WPF设计学习记录滴滴滴2
学习·wpf
m0_555762901 小时前
多人协同进行qt应用程序开发应该注意什么?
开发语言·qt