1. 目录和文件
1.1 目录操作
-
QDir 类用来处理目录
-
常用方法:
-
QDir(QString path) : 实例化
-
absolutePath() : 获取目录绝对路径
-
dirName() : 获取目录相对路径
-
exists(dirPath) : 判断目录是否存在
-
mkdir(QString dirPath) : 创建目录
-
rmdir(QString dirPath) : 删除目录
-
示例:
cpp
// 实例化对象,并将当前目录的相对路径传入
QDir dir("./");
// 打印 绝对路径 和 项目路径
qDebug() << dir.absolutePath() << dir.dirName();
// 在当前目录下创建 abc 目录
if (dir.exists("./abc") == false)
{
bool ret = dir.mkdir("abc");
qDebug() << (ret == true ? "创建成功" : "创建失败");
}
else
{
qDebug() << "目录已存在";
}
// 删除当前目录下的 abc 目录
dir.rmdir("abc");
其他方法:
-
entryInfoList({文件类型}) : 获取指定目录下所有的目录和文件,返回值 QFileInfoList
-
QList<QFileInfo> <===> QFileInfoList
-
entryInfoList({"*.jpg", "*.png"})
-
-
setFilter() : 过滤获取的文件和目录类型
-
QDir::Dirs : 保留目录
-
QDir::Files :保留文件
-
QDir::NoSymLinks : 不要快捷方式
-
QDir::NoDotAndDotDot : 不要 . 和 ..
-
1.2 文件信息
QFileInfo 类用来获取文件信息
cpp
QFileInfo info("./Makefile.Debug");
qDebug() << "文件绝对路径: " << info.absoluteFilePath();
qDebug() << "文件全名:" << info.fileName();
qDebug() << "文件名:" << info.baseName();
qDebug() << "文件后缀:" << info.suffix();
qDebug() << "文件大小:" << info.size();
qDebug() << "创建时间:" << info.birthTime().toString("yyyy-MM-dd hh:mm:ss");
qDebug() << "是否为目录:" << info.isDir();
qDebug() << "是否为文件:" << info.isFile();
// 获取文件所在目录的路径
QDir filePath = info.dir();
qDebug() << filePath;
案例: 获取路径下所有的文件和目录,并将文件和目录的信息输出在控制台
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QDir>
#include <QDebug>
#include <QFileInfoList>
#include <QFileInfo>
#include <QDateTime>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
this->getDirInfo(".");
// qDebug() << this->getDirSize(".");
}
int Widget::getDirSize(QString path)
{
int totalSize = 0;
QDir dir(path);
dir.setFilter(QDir::Dirs | QDir::Files | QDir::NoSymLinks | QDir::NoDotAndDotDot);
QFileInfoList l = dir.entryInfoList();
for(QFileInfo item : l)
{
if (item.isFile())
{
//如果是文件 ,直接累加
totalSize += item.size();
}
else if (item.isDir()) {
totalSize += this->getDirSize((item.absoluteFilePath()));
}
}
return totalSize;
}
void Widget::getDirInfo(QString path)
{
QDir dir(path);
dir.setFilter(QDir::Dirs | QDir::Files | QDir::NoSymLinks | QDir::NoDotAndDotDot);
QFileInfoList list = dir.entryInfoList();
for (QFileInfo item : list)
{
if(item.isDir())
{
qDebug() << "dir" << item.fileName() << this->getDirSize(item.absoluteFilePath()) << item.birthTime().toString("yyyy-MM-dd");
}
else {
qDebug() << "file" << item.fileName() << item.size() << item.birthTime().toString("yyyy-MM-dd");
}
}
}
1.3 写文件
-
QFile 类用来对文件进行读写
-
常用方法:
-
open(打开方式) : 打开文件
-
QIODevice::WriteOnly 已只写方式打开,新内容会覆盖原来的内容
-
QIODevice::ReadWrite 已读写方式打开, 打开时光标在文件头部,内容从文件头开始追加
-
QIODevice::Append 已追加方式打开,打开时光标在文件尾部,内容从尾部开始追加
-
-
write(QByteArray) : 向文件中写入内容 , 返回值得到写入的字符串长度
-
向文件中写入内容 (覆盖写入)
cpp
QFile file("e:/a.txt");
if (file.open(QIODevice::WriteOnly))
{
QByteArray str = "Hello World!! 你好啊!!";
qint64 len = file.write(str);
qDebug() << "写入内容的长度" << len;
}
else
{
qDebug() << "写文件失败";
}
向文件头部追加内容 (头部写入)
cpp
if (file.open(QIODevice::ReadWrite))
{
QByteArray str = "头部~~~";
qint64 len = file.write(str);
qDebug() << "写入内容的长度" << len;
}
else
{
qDebug() << "追加到文件头部失败";
}
向文件尾部追加内容 (追加写入)
cpp
if (file.open(QIODevice::Append))
{
QString str = "~~~~尾部追加";
qint64 len = file.write(str.toUtf8().data());
qDebug() << "写入内容的长度" << len;
}
else
{
qDebug() << "追加到文件尾部失败";
}
1.4 读文件
QIODevice::ReadOnly : 已只读方式打开
-
readAll() : 一次性去读文件的所有内容
-
readLine() : 一次读取一行内容
示例1: 一次性读取文件中所有的内容:
- readAll() : 一次性读取文件中所有的内容
cpp
QFile file("e:/a.txt");
if (file.open(QIODevice::ReadOnly))
{
// 一次性读取文件所有内容
QByteArray b = file.readAll();
qDebug() << QString(b);
}
else
{
qDebug() << "读文件失败";
}
示例2: 按行读取文件中的内容
核心方法:
-
atEnd() : 判断是否已将到文件结尾
-
readLine(*char, length) : 一次性读取 length 指定长度的内容,并保存到 char 中
cpp
QFile file("e:/aaa.txt");
file.open(QIODevice::ReadOnly);
while (!file.atEnd()) {
char buf[1024];
qint64 len = file.readLine(buf, sizeof(buf));
qDebug() << len << buf;
}
2. 综合案例
2.1 利用ui来创建界面
-
菜单栏
-
工具栏
只能显示在上方,不能移动不能悬浮
可以修改图标大小
3.中心部件
状态栏: 只能使用代码来创建
cpp
// 窗口基本设置
ui->setupUi(this);
this->setFixedSize(750, 500);
this->setWindowTitle("记事本");
// 设置状态栏
QLabel *statusLb = new QLabel;
QString str = QString("文本长度: %1").arg(10);
statusLb->setText(str);
ui->statusBar->addWidget(statusLb);
2.2 文件菜单
2.2.1 退出
在 "退出" 按钮上设置信号 (triggered),触发 close 槽函数即可。
cpp
// 快捷键设置
ui->actionExit->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q));
// 链接信号和槽,实现关闭功能
connect(ui->actionExit, &QAction::triggered, this, &MainWindow::close);
2.2.2 另存为
实现思路:
- 判断多行文本框中是否有内容
没有则不进行操作
-
如果有,则弹出文件保存框
-
点击文件保存框的 "保存" 按钮时,获取文件路径,获取多行文本框中的内容,将内容写入文件
cpp
// 另存为
ui->actionSaveAs->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_S));
connect(ui->actionSaveAs, &QAction::triggered, [=](){
// 1) 获取文本框中的内容
QString tmp = ui->textEdit->toPlainText();
// 2) 判断内容是否为空
if (!tmp.isEmpty())
{
// 3) 当不为空时打开文件保存框
filePath = QFileDialog::getSaveFileName(this, "保存文件", "./", "*.txt");
// 4) 当文件路径不为空时进行文件写入操作
if (!filePath.isEmpty())
{
// 实例化 QFile 并以只写方式打开文件
QFile file(filePath);
bool res = file.open(QIODevice::WriteOnly);
// 当打开正确时,进行文件写入操作
if (res == true)
{
file.write(tmp.toUtf8());
}
else
{
QMessageBox::critical(this, "严重错误", "保存的文件路径出错");
}
}
}
});
2.2.3 保存
实现思路:
-
判断多行文本框中是否有内容; 如果没有则不做任何操作
-
如果有,判断是否有文件路径;
① 如果没有,则弹出文件保存框,获取文件路径,进行写入操作
② 如果有,覆盖写入
注意事项:
在头文件中加入了 filePath 属性,获取的文件保存路径都要放在 filePath 属性中(包括另存为时使用filePath)
cpp
ui->actionSave->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_S));
connect(ui->actionSave, &QAction::triggered, [=](){
QString tmp = ui->textEdit->toPlainText();
if (!tmp.isEmpty())
{
// 判断保存文件的文件路径是否为空
if (filePath.isEmpty())
{
// 为空时,说明内容还没有保存,需要弹出保存框
// 不为空时,说明内容曾经保存过,不需要弹出保存框,直接写文件即可
filePath = QFileDialog::getSaveFileName(this, "保存文件", "./", "*.txt");
}
QFile file(filePath);
file.open(QIODevice::WriteOnly);
file.write(tmp.toUtf8());
}
});
2.2.4 打开
核心功能:
-
点击 "打开" 按钮时,弹出文件打开框,选中文件,得到文件路径
-
根据文件路径,读取文件中的内容,再显示到 textEdit 上
扩展功能:
-
判断 textEdit 中是否有内容,如果没有则直接执行打开
-
如果有,则弹出询问框。 如果点击 取消,则执行开大
-
如果选择 保存, 执行保存的流程
cpp
ui->actionOpen->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_O));
connect(ui->actionOpen, &QAction::triggered, [=](){
// 获取文本框中的内容
QString tmp = ui->textEdit->toPlainText();
if (!tmp.isEmpty())
{
// 不为空时,要提示是否保存
int res = QMessageBox::question(this, "询问", "内容尚未保存,您需要进行保存吗?", QMessageBox::Save | QMessageBox::Cancel);
if (QMessageBox::Save == res)
{
if (!tmp.isEmpty())
{
// 判断保存文件的文件路径是否为空
if (filePath.isEmpty())
{
// 为空时,说明内容还没有保存,需要弹出保存框
// 不为空时,说明内容曾经保存过,不需要弹出保存框,直接写文件即可
filePath = QFileDialog::getSaveFileName(this, "保存文件", "C:/Users/Administrator/Documents", "*.txt");
}
QFile file(filePath);
file.open(QIODevice::WriteOnly);
file.write(tmp.toUtf8());
}
}
}
// 打开文件选择框,获取文件路径
filePath = QFileDialog::getOpenFileName(this, "打开文件", "./", "*.txt");
if (!filePath.isEmpty())
{
// 如果文件路径不为空,则读取文件的内容,显示到 textEdit 中
QFile file(filePath);
file.open(QIODevice::ReadOnly);
QByteArray b = file.readAll();
ui->textEdit->setText(b);
}
});
2.2.5 新建
实现思路:
-
判断文本区域是否有内容
-
如果没有,则将 filePath 置空,同时将文本区域也置空
-
如果有,则弹出询问框,询问是否需要保存。
① 选择取消时,则将 filePath 置空,同时将 文本区域也置空
② 选择保存时,弹出文件保存框。得到文件路径并将其保存在 filePath 中,再获取文本区域的内容,进行写文件操作。
cpp
connect(ui->actionNew, &QAction::triggered, [=](){
// 获取当前文本区域的内容
QString tmp = ui->textEdit->toPlainText();
// 判断文本区域的内容是否为空
if (!tmp.isEmpty())
{
// 不为空的时候弹出询问框进行询问提示, 利用 QMessageBox 的构造函数实现中文按钮的设置
// 参数1: 设置弹出框的类型 和 图标设置
// 参数2: 弹出框标题栏的文本
// 参数3: 弹出框正文区域的文本
// 参数4: 设置使用哪些按钮
QMessageBox myBox(
QMessageBox::Question,
"是否保存",
"您需要保存当前内容吗?",
QMessageBox::Save | QMessageBox::Cancel
);
// 利用两种方式修改按钮中的文字
myBox.setButtonText(QMessageBox::Save, "保存");
myBox.button(QMessageBox::Cancel)->setText("取消");
// 调用 exec 方法弹出询问框
// 返回值: int类型,但是本质是选中的按钮的枚举值
int result = myBox.exec();
// 判断是否点击的是保存按钮
if (result == QMessageBox::Save)
{
// 判断保存文件的文件路径是否为空
if (filePath.isEmpty())
{
// 为空时,说明内容还没有保存,需要弹出保存框
// 不为空时,说明内容曾经保存过,不需要弹出保存框,直接写文件即可
filePath = QFileDialog::getSaveFileName(this, "保存文件", "./", "*.txt");
}
QFile file(filePath);
// 打开文件的方式 WriteOnly | ReadWrite | Append | ReadOnly
file.open(QIODevice::WriteOnly);
file.write(tmp.toUtf8());
}
}
// 清空操作
filePath = "";
ui->textEdit->setText("");
});
2.3 编辑菜单
利用 QTextEdit 的槽函数即可
cpp
// 复制
connect(ui->actionCopy, &QAction::triggered, ui->textEdit, &QTextEdit::copy);
// 粘贴
connect(ui->actionPaste, &QAction::triggered, ui->textEdit, &QTextEdit::paste);
// 剪切
connect(ui->actionCut, &QAction::triggered, ui->textEdit, &QTextEdit::cut);
// 撤销
connect(ui->actionUndo, &QAction::triggered, ui->textEdit, &QTextEdit::undo);
2.4 格式菜单
2.4.1 自动换行
实现思路:
-
设置一个标记来标志当前是否为 换行状态 (true 换行 | false 不换行)
-
显示图标、自动换行
-
在 "自动换行" 菜单上注册信号,判断状态,如果为true则显示图标,自动换行;反之,不显示图标,不换行
核心方法:
-
QTextEdit::setLineWrapMode(QTextEdit::WidgetWidth | QTextEdit::NoWrap)
-
QTextEdit::WidgetWidth 自动换行
-
QTextEdit::NoWrap 不自动换行
-
实现:
1.在头文件中定义状态
cpp
class MainWindow : public QMainWindow
{
...
private:
Ui::MainWindow *ui;
QString filePath;
// 自动换行的标记,true 是自动换行 | false 是不自动换行
bool warpFlag = true;
};
2.在源文件中实现
cpp
// 程序首次运行时进行初始化状态
ui->actionWrap_2->setIcon(QIcon(":/icon/r.png"));
// QTextOption::NoWrap (不自动换行) | QTextOption::WordWrap (自动换行)
ui->textEdit->setLineWrapMode(QTextEdit::WidgetWidth);
// 点击 "自动换行" 时触发信号
connect(ui->actionWrap_2, &QAction::triggered, [&](){
// 对状态取反
warpFlag = !warpFlag;
// 判断自动换行的状态
if (warpFlag == false)
{
// 如果为false时不换行,则设置图标为空,自动换行的状态为 NoWrap
ui->actionWrap_2->setIcon(QIcon(""));
ui->textEdit->setLineWrapMode(QTextEdit::NoWrap);
}
else
{
// 如果为true时自动换行,设置图标,自动换行状态为 WordWrap
ui->actionWrap_2->setIcon(QIcon(":/icon/r.png"));
ui->textEdit->setLineWrapMode(QTextEdit::WidgetWidth);
}
});
2.4.2 字体
实现思路:
-
点击 "字体" 时,弹出字体选择框(QFontDialog)
-
当用户点击 "确定" 按钮时,获取选中的字体数据
-
将字体数据设置到 textEdit 中
cpp
connect(ui->actionFont, &QAction::triggered, [=](){
bool flag = true;
// 打开字体选择框,返回值能够保存用户选择的字体数据
QFont font = QFontDialog::getFont(&flag);
// 将字体数据设置到 textEdit 中
ui->textEdit->setFont(font);
});
2.5 状态栏
目标: 显示文本长度
实现步骤:
-
使用 QTextEdit 的 textChanged 信号,该信号是一旦文本框中的内容发送变化就会触发
-
获取 QTextEdit 中内容,使用 size 方法来获取内容的长度,最后再将长度显示到 状态栏的QLabel中
cpp
// 在头文件中声明了状态栏中的QLabel
class MainWindow : public QMainWindow
{
....
private slots:
// textChanged 匹配的槽函数
void on_textEdit_textChanged();
private:
Ui::MainWindow *ui;
QString filePath;
bool warpFlag = true;
// 状态栏中使用 QLabel
QLabel *statusLb;
};
cpp
void MainWindow::on_textEdit_textChanged()
{
// 获取文本长度
int len = ui->textEdit->toPlainText().size();
QString str = QString("文本长度: %1").arg(len);
statusLb->setText(str);
}
2.6 查看菜单
2.6.1 状态栏
逻辑同 自动换行
cpp
// 是否显示状态栏
connect(ui->actionStatus, &QAction::triggered, [=](){
isStatusLbShow = !isStatusLbShow;
if (isStatusLbShow == true)
{
statusLb->show();
}
else
{
statusLb->hide();
}
});
2.6.2 放大&缩小
实现步骤:
- 在头文件中对字体数据进行单独声明 (family、pointSize、italic、bold)
优势: 1. 可以在程序首次运行时对 textEdit 中的字体进行设置 2. 方便单独对字体进行放大和缩小的操作
-
在源文件中,初始化 textEdit 中的字体数据
-
点击 "放大" 和 "缩小" 时,单独设置 pointSize 的值
cpp
class MainWindow : public QMainWindow
{
......
private:
....
// 设置字体数据
QString family = "微软雅黑";
int pointSize = 16;
bool italic = true;
bool bold = true;
};
cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
.....
// 设置字体
ui->textEdit->setFont(QFont(family, pointSize, italic, bold));
....
}
cpp
// 放大
connect(ui->actionZoomUp, &QAction::triggered, [=](){
pointSize += 2;
ui->textEdit->setFont(QFont(family, pointSize, italic, bold));
});
// 缩小
connect(ui->actionZoomDown, &QAction::triggered, [=](){
pointSize -= 2;
ui->textEdit->setFont(QFont(family, pointSize, italic, bold));
});
bug 修补: 在 "格式" 菜单中调整字体之后,需要将最新的字体数据保存在属性中,否则放大和缩小会出错
cpp
// 字体
connect(ui->actionFont_2, &QAction::triggered, [=](){
bool flag = true;
QFont font = QFontDialog::getFont(&flag);
ui->textEdit->setFont(font);
// 将最新的字体数据保存
family = font.family();
pointSize = font.pointSize();
italic = font.italic();
bold = font.bold();
});
2.7 帮助菜单
打开浏览器
cpp
connect(ui->actionHelpDoc, &QAction::triggered, [](){
QDesktopServices::openUrl(QUrl("https://www.baidu.com"));
});