Qt 5.14.2 学习记录 —— 이십 QFile和多线程
文章目录
- 1、QFile
- 1、打开
- 2、读写
- 3、关闭
- 4、程序
- 5、其它功能
- 2、多线程
- 1、演示
- 2、锁
- 3、条件变量和信号量
1、QFile
Qt有自己的一套文件体系,不过Qt也可以使用C++,C,Linux的文件操作。使用Qt的文件体系和Qt自己的一些类型更好配合。
管理写入读取的就是Qt中的QIODevice类。QProcess相当于是对fork/exec操作进行的封装;QTemporaryFile表示临时文件,用完就销毁,文件也就删除;在写大量数据时,要先创建一个临时文件,将旧文件内容写到临时文件里,写完后再删除旧文件,这就是QSaveFile的操作。
// 这里的name用绝/相对路径
QFile(const QString& name)
// 查看文档来查看打开、读写、关闭文件操作接口
1、打开
不过实际用的是这个,它可以直接拿到之前设置的路径
关于OpenMode
2、读写
QByteArray容易转QString。
3、关闭
关闭时就是在释放文件描述符表中的表项,文件描述符表存在上限。
4、程序
// mainwindow.h
#include <QMainWindow>
#include <QPlainTextEdit>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
void handle1();
void handle2();
private:
Ui::MainWindow *ui;
QPlainTextEdit* edit;
};
// mainwindow.cpp
#include <QDebug>
#include <QFileDialog>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->setWindowTitle("窗口");
QMenuBar* menuBar = this->menuBar();
QMenu* menu = new QMenu("文件");
menuBar->addMenu(menu);
// 形成菜单
QAction* action1 = new QAction("打开");
QAction* action2 = new QAction("保存");
menu->addAction(action1);
menu->addAction(action2);
// 指定输入框
edit = new QPlainTextEdit();
QFont font;
font.setPixelSize(20);
edit->setFont(font);
this->setCentralWidget(edit);
connect(action1, &QAction::triggered, this, &MainWindow::handle1);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::handle1()
{
// 弹出打开文件对话框
QString path = QFileDialog::getOpenFileName(this);
// 文件名显示到状态栏
QStatusBar* statusBar = this->statusBar();
statusBar->showMessage(path);
// 通过路径构造QFile对象
QFile file(path);
bool ret = file.open(QIODevice::ReadOnly);
if (!ret)
{
statusBar->showMessage(path + " 打开失败!");
return ;
}
// 读取文件
// 即使返回值是QByteArray, 也可以直接用QString接收
// 但前提必须不是二进制文件, 是文本文件
QString text = file.readAll();
file.close();
// 读到的内容设置到输入框中
edit->setPlainText(text);
}
void MainWindow::handle2()
{
QString path = QFileDialog::getSaveFileName(this);
QStatusBar* statusBar = this->statusBar();
statusBar->showMessage(path);
QFile file(path);
bool ret = file.open(QFile::WriteOnly);
if (!ret)
{
statusBar->showMessage(path + " 打开失败!");
return ;
}
const QString& text = edit->toPlainText();
// 转成QByteArray
file.write(text.toUtf8());
file.close();
}
5、其它功能
QFileInfo可以获取到Qt的文件的相关属性。
void Widget::on_pushButton_clicked()
{
QString path = QFileDialog::getOpenFileName(this);
QFileInfo fileInfo(path); // 构造QFileInfo对象
qDebug() << fileInfo.fileName();
qDebug() << fileInfo.suffix();
}
2、多线程
和Linux的多线程本质是一样的。Linux有pthread库,C++11有std::thread,Qt也封装了线程库,参考了Java的线程库。
创建线程要创建QThread对象,并创建一个QThread的子类,重写父类的run函数来作为线程的入口函数。
start就是调用系统API来创建线程,创建好后自动执行run函数。
1、演示
创建QWidget项目,通过线程完成定时器功能。
将intValue属性改为10。
创建新的子类
// thread.h
#include <QWidget>
#include <QThread>
class Thread : public QThread
{
Q_OBJECT
public:
Thread();
void run();
signals:
void notify();
};
// widget.h
#include <QWidget>
#include "thread.h"
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
void handle();
private:
Ui::Widget *ui;
Thread thread;
};
// thread.cpp
#include "thread.h"
Thread::Thread()
{
}
void Thread::run()
{
// 由于Qt的线程策略, 不允许多个线程同时修改界面
// run实现计时效果
// 每过一秒钟, 通过信号槽通知主线程修改界面
for(int i = 0; i < 10; ++i)
{
sleep(1);
emit notify();
}
}
// widget.cpp
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
connect(&thread, &Thread::notify, this, &Widget::handle);
thread.start();
}
void Widget::handle()
{
int value = ui->lcdNumber->intValue();
--value;
ui->lcdNumber->display(value);
}
2、锁
Qt的锁是QMutex,lock和unlock方法。
QWidget项目,创建继承QThread的类Thread。
// thread.h
#include <QWidget>
#include <QThread>
class Thread : public QThread
{
Q_OBJECT
public:
Thread();
// 声明
static int num;
void run();
};
// thread.cpp
#include "thread.h"
// 定义
int Thread::num = 0;
Thread::Thread()
{
}
void Thread::run()
{
for(int i = 0; i < 47000; ++i)
{
++num;
}
}
// widget.cpp
#include <QDebug>
// widget.h中引入thread.h
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
Thread t1;
Thread t2;
t1.start();
t2.start();
// 线程等待
// 如果不等待, 那么除了这两个, Widget这个主线程还在并发执行
// t1t2开始了, 但是Widget不停, 那么很快就执行到了打印, 此时结果肯定不大
t1.wait();
t2.wait();
qDebug() << Thread::num;
}
这样肯定不会打印出47000 * 2的数字。
加锁
// thread.h
#include <QMutex>
public:
// 声明
static int num;
static QMutex mutex;
// thread.cpp
// 定义
int Thread::num = 0;
QMutex Thread::mutex;
void Thread::run()
{
for(int i = 0; i < 47000; ++i)
{
mutex.lock();
++num;
mutex.unlock();
}
}
Qt中的智能指针是QMutexLocker,C++ 11中则是std::lock_guard。
// thread.cpp
#include "thread.h"
#include <QMutexLocker>
// 定义
int Thread::num = 0;
QMutex Thread::mutex;
void Thread::run()
{
for(int i = 0; i < 47000; ++i)
{
QMutexLocker locker(&mutex);
++num;
}
}
Qt还有别的锁
3、条件变量和信号量
QWaitCondition条件变量类,有wait,wake,wakeAll方法
例子
QMutex mutex;
QWaitCondition condition;
//在等待线程中
mutex.lock();
//检查条件是否满足, 若不满足则等待
while (!conditionFullfilled())
{
condition.wait(&mutex); //等待条件满足并释放锁
}
//条件满足后继续执行
//...
mutex.unlock();
//在改变条件的线程中
mutex.lock();
//改变条件
changeCondition();
condition.wakeAll(); //唤醒等待的线程
mutex.unlock();
QSemaphore信号量类
QSemaphore semaphore(2); //同时允许两个线程访问共享资源
//在需要访问共享资源的线程中
semaphore.acquire(); //尝试获取信号量,若已满则阻塞
//访问共享资源
//...
semaphore.release(); //释放信号量
//在另⼀个线程中进行类似操作
结束。