嵌入式学习-QT-Day07
[5.1 复现程序未响应](#5.1 复现程序未响应)
[5.2 创建并启动一个子线程](#5.2 创建并启动一个子线程)
[5.3 异步刷新](#5.3 异步刷新)
[5.4 线程停止](#5.4 线程停止)
七、文件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]
- 在run函数的函数体中编写子线程要执行的耗时操作
- 创建自定义子线程对象,并调用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();`
`}