C++ QT入门1——记事本基础功能实现(基本控件布局+信号与槽+文件类操作)
- 一、UI界面的基础设置
- 样式表通用配置
- 通过样式表设置按钮三态
- 以图片作为按钮背景
- 二、按键响应——☆信号与槽
- 信号与槽基本概念
- 按键QPushButton设置信号与槽
- 自定义信号与槽
- 自定义信号与槽的实际应用——页面的跳转
- 三、文件类操作QFile
- 文件的直接读写
- 通过文件流读写数据
- QFileDialog 文件选择框
- 打开文件
- 创建 / 保存文件
- 四、记事本基本功能整合
- 行编辑器与文本编辑器
- 文件的打开
- 文件的保存
- 关闭按钮的实现
一、UI界面的基础设置
关于UI界面的设计与美化须通过不断熟悉与操作,具体可参考:白月黑羽-页面设计与布局 系列教程进行实操锻炼。
样式表通用配置
font-size:40px; /*字体大小*/
font: 10pt "微软雅黑"; /* 字体大小及格式*/
border:0px solid white; /*边框粗细及颜色*/
border-radius:0px; /*边框半径*/
/*设置字体颜色方式*/
color: red;
color: #FF00FE;
color: rgba(255, 0, 255, 255); /*其中a表示透明度*/
/*设置背景颜色方式*/
background-color: blue
background-color: #00FFEE
background-color: rgba(255, 255, 0, 255);
可通过模拟设计一个自带的计算器来练手UI设计,后续也可继续优化计算机具体功能。
通过样式表设置按钮三态
/*按钮正常状态*/
QPushButton{
background-color: rgba(220, 250, 220, 255);
border:0px solid white;
border-radius:0px;
font-size:60px;
}
/*按钮悬停状态*/
QPushButton:hover{
background-color: rgba(255, 255, 0, 255);
border:0px solid white;
border-radius:0px;
color:#FF00FE;
font-size:60px;
}
/*按钮按下状态*/
QPushButton:pressed{
background-color: rgba(255, 255, 0, 255);
border:0px solid white;
border-radius:0px;
color: #FF00FE;
font-size:60px;
}
以图片作为按钮背景
资源文件:
如果你的程序需要加载特定的资源(图标、文本翻译等),那么,将其放置在资源文件中,就再也不需要担心这些文件的丢失。
加载图片资源
设置窗口标题及图标
添加图片
样式表->添加资源->border-image
将图片资源放入按钮三态
二、按键响应——☆信号与槽
信号与槽基本概念
在Qt中,信号和槽是一种非常强大的事件通信机制,理解信号与槽对于编写Qt程序至关重要。
概要:
1. 信号(Signals): 是由对象在特定事件发生时发出的消息。例如,QpushButton 有一个 clicked()信号,当用户点击按钮时发出。
2. 槽(Slots): 是用来响应信号的方法。一个槽可以是任何函数,当其关联的信号被发出时,该槽的函数被调用。
3. 连接信号和槽:使用 Qobject::connect()方法将信号连接到槽。当信号发出时,关联的槽函数会自动执行。
按键QPushButton设置信号与槽
连接方式 | 描述 | 示例 |
---|---|---|
自动连接(使用UI文件) | 在使用Qr Designer时,可以通过命名约定自动连接信号和槽。当UI文件自动加载 时,以on_ objectName_ signalName 命名的槽会自动连按到相应的信号 | 在Qt Designer中命名按钮为pushButton,然后在代码中定义on_pushButton_clicked() . |
使用QObject::connect | 最常用的方式,直接通过 QObject::connect 函数连接信号和槽 | QObject::connect(sender, SIGNAL(signal()), receiver, SLOT(s1ot())); |
使用C++11 Lambda表达式 | 利用C++11引入的Lambda表达式进行信号与槽的连接。这种方式可以直接在连接点使用匿名函数,使代码更加简洁 | QObject::connect(sender, &Sender::signal, = { /* lambda body */ }); |
使用函数指针 | Qt5中引入,允许使用函数指针直接连接信号和槽,这种方式类型安全,且可以利用IDE的代码不全和错误检测 | QObject::connect(sender, &Sender::signal, receiver, &Receiver::s1ot); |
1. 自动连接:
在UI文件内选择按钮右键->转到槽,选择触发信号即可自动连接到槽函数
2. 使用QObject::connect:
//头文件声明槽函数
private slots:
void pushButton5_clicked();
//构造函数内信号连接槽
//QObject::connect(sender, SIGNAL(signal()), receiver, SLOT(s1ot()));
QObject::connect(ui->pushButton5, SIGNAL(clicked()), this, SLOT(pushButton5_clicked()));
//2. 第2种方式 槽函数 .cpp外部声明
void Widget::pushButton5_clicked()
{
cout << "button5 ckicked()" << endl;
}
3. 使用Lambda表达式:
//3. 第3种方式 lambda表达式 QObject::connect(sender, &Sender::signal, [=]() { /* lambda body */ });
QObject::connect(ui->pushButton6, &QPushButton::clicked,[=](){
cout << "button6 ckicked()" << endl;
});
4. 使用函数指针
//头文件声明槽函数
private slots:
void on_pushButton7_clicked();
//构造函数内信号连接槽
//4. 第4种方式 QObject::connect(sender, &Sender::signal, receiver, &Receiver::s1ot);
QObject::connect(ui->pushButton7, &QPushButton::clicked, this, &Widget::on_pushButton7_clicked);
//4. 第4种方式 外部实现槽函数
void Widget::on_pushButton7_clicked()
{
cout << "button4 ckicked()" << endl;
}
自定义信号与槽
在Qt中,自定义信号与槽是实现对象间通信的一种机制。信号与槽Qt对象通信的核心特性,使得一个对象能够在发生某种事件时通知其他对象。自定义信号与槽的实现步骤如下:
1. 定义信号: 在Qt中,信号是由 signals
关键字声明类的成员函数,不需要实现,只需要声明
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
signals:
void mysignal(); //自定义信号
void mysignalPara(int val);//自定义信号带参数
};
在上述程序中,Widget 类中有两个信号 mysignal()
和 mysignalPara(int val)
,其中mysignalPara(int val)信号带参数
2. 定义槽: 槽函数可以是任何普通的成员函数,但通常在定义类中用 slots
关键字标识。槽函数可以有返回类型,也可以接受参数,但他们的参数类型需要与发出信号的参数类型匹配。例如:
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void myslot(); //自定义槽
void myslotPara(int val); //自定义槽带参数
private:
Ui::Widget *ui;
};
3. 连接信号与槽: 使用 QObject::connect 函数将信号与槽连接起来。当信号被发射时,连接到这个信号的槽被调用,通常在构造函数内进行连接
//绑定信号与槽
connect(this, SIGNAL(mysignal()), this, SLOT(myslot()));
connect(this, SIGNAL(mysignalPara(int)), this, SLOT(myslotPara(int)));
这段代码分别将信号 mysignal
和 mysignalPara
连接到槽函数 myslot
和 myslotPara
4. 发射信号: 使用 emit
关键字发射信号。当信号被发射时,所有连接到这个信号的槽会被调用。
emit mysignal(); //发送信号
emit mysignalPara(123); //发送信号
这将触发所有连接到这两个信号上的槽
自定义信号和槽是Qt编程中非常强大的特性,他们似的组件之间的通信变得灵活而松耦合。通过信号和槽,可以方便实现各种复杂的事件驱动逻辑。
自定义信号与槽的实际应用——页面的跳转
对于从页面A跳转到页面B,只需要点击页面A上的按钮,隐藏页面A,打开页面B即可,而对于如何从页面B切换回页面A则可通过自定义信号和槽实现。
页面B上的按钮点击后发射信号,页面A实时检测自定义信号,检测到到页面B中的按钮发出的自定义信号时,隐藏页面B,显示页面A。
思路流程图及伪代码实现:
切换到新窗口:
//页面切换
//创建新窗口
SecondWindow *secondWindow = new SecondWindow();
connect(ui->pushButton8,&QPushButton::clicked,this,[=](){
secondWindow->show();
this->hide();
});
切换回原窗口:
secondwindow.h
signals:
void btn_clicked(); //按键按下发送的自定义信号
secondwindow.cpp: 按下返回按钮,发送返回信号供主窗口接收
//按下返回按钮,发送返回信号供主窗口接收
connect(ui->pushButton, &QPushButton::clicked,[=](){
//emit 窗口发信号
emit btn_clicked();
});
firstwindow.cpp: 接收返回按钮发送的返回信号
//接收B窗口发送的信号
connect(secondWindow,&SecondWindow::btn_clicked,[=](){
secondWindow->hide();
this->show();
});
三、文件类操作QFile
主要功能:
- 文件读取: QFile 支持打开文件进行读取或写入操作
- 文件信息: 可以检索有关文件的信息,如大小、修改日期等
- 文件操作: 提供了对文件进行重命名、移动、 删除等操作的能力
- 错误处理: QFile 在操作文件时提供了错误处理机制,可以通过相应的函数检查和获取错误信息
文件的直接读写
常用方法 | 功能 |
---|---|
open(flags) | 打开一个文件,需要指定模式(如只读、只写、 读写等) |
close() | 关闭文件 |
read(char *data, qint64 maxlen) 和 write(char *data, qint64 maxlen) | 用于读取和写入数据 |
exists() | 检查文件是否存在 |
remove() | 删除文件 |
copy() | 复制文件 |
size() | 文件大小,字符数 |
文件读取:
void Widget::on_pushButton1_clicked()
{
//1. 打开文件
//加载文件 这里要加绝对路径
QFile file("E:/qtProject/00_QT_CLC/notebook2/data.txt");
//打开文件 只读 TXT类型
if(!file.open(QIODevice::ReadOnly | QIODevice::Text))
qDebug() << "file open error" << endl;
//2. 读取文件
int size = file.size();
char* context = new char(size);
int ret = file.read(context,100);
if(ret == -1){
qDebug() << "file read error" << endl;
return;
}
//3. 输出文件内容并关闭
cout << context << endl;
file.close();
}
文件写入:
//保存文件按钮
void Widget::on_pushButton3_clicked()
{
//1. 打开文件
//加载文件 这里要加绝对路径
QFile file("E:/qtProject/00_QT_CLC/notebook2/data.txt");
//打开文件 可读可写 文本模式 覆盖写
if(!file.open(QIODevice::ReadWrite | QIODevice::Text| QIODevice::Truncate))
qDebug() << "file open error" << endl;
//2. 写入文件
char context[] = "hello world";
int ret = file.write(context,strlen(context));
if(ret == -1){
qDebug() << "file write error" << endl;
return;
}
//3. 关闭文件
file.close();
}
通过文件流读写数据
文件的读取:
void Widget::on_pushButton1_clicked()
{
//加载文件 这里要加绝对路径
QFile file("E:/qtProject/00_QT_CLC/notebook2/data.txt");
//打开文件 只读 TXT类型
if(!file.open(QIODevice::ReadOnly | QIODevice::Text))
qDebug() << "file open error" << endl;
//读取文件数据流
QTextStream in(&file); //绑定文件与流
in.setCodec("UTF-8");
while(!in.atEnd())
{
QString line = in.readLine();
qDebug() << line << endl;
}
file.close(); //关闭文件
}
文件的写入:
//保存文件按钮
void Widget::on_pushButton3_clicked()
{
//1. 打开文件
//加载文件 这里要加绝对路径
QFile file("E:/qtProject/00_QT_CLC/notebook2/data.txt");
//打开文件 可读可写 文本模式 覆盖写
if(!file.open(QIODevice::ReadWrite | QIODevice::Text| QIODevice::Truncate))
qDebug() << "file open error" << endl;
//以数据流写数据
QTextStream out(&file); //绑定文件与流
out.setCodec("UTF-8");
out << "abcdef" << "\n";
file.close(); //关闭文件
}
QFileDialog 文件选择框
打开文件
使用QFileDialog的基本流程步骤通常如下:
实例化对象: 首先,创建一个 QFileDialog
的对象实例
//1. 实例化对象
QFileDialog dialog;
设置模式: 根据需要设置对话框的模式,如打开文件、保存文件等
//设置模式 setFileMode
//QFileDialog::AnyFile 任何文件
//QFileDialog::ExistingFile 存在的文件
//QFileDialog::Directory 文件夹
//QFileDialog::ExistingFiles 存在的多个文件
dialog.setFileMode(QFileDialog::ExistingFiles);
设置过滤器: 如果需要,可以设置文件类型过滤器,以限制用户可以选择的文件夹类型
//设置文件类型过滤器 setNameFilter
//dialog.setNameFilter("*.txt");
dialog.setNameFilter(tr("Images (*.png *.xpm *.jpg);;Text files (*.txt);;XML files (*.xml)"));
显示对话框: 通过调用 exec()
方法显示对话框,并在用户作出选择后执行相应的操作
//显示对话框
dialog.exec();
//选择文件
//选择的文件名称存入列表
QStringList qstrings = dialog.selectedFiles();
for(QString str : qstrings) // 迭代器遍历qstrings
{
qDebug() << str << endl;
}
程序实例:
//分步骤打开
//1. 实例化对象
QFileDialog dialog;
//设置模式 setFileMode
//QFileDialog::AnyFile 任何文件 ExistingFile :存在的文件 Directory 目录 ExistingFiles 存在的多个文件
dialog.setFileMode(QFileDialog::ExistingFile);
//设置文件类型过滤器 setNameFilter
//dialog.setNameFilter("*.txt");
dialog.setNameFilter(tr("Images (*.png *.xpm *.jpg);;Text files (*.txt);;XML files (*.xml)"));
//显示对话框
dialog.exec();
//选择文件
QStringList fileName = dialog.selectedFiles();
//1. 打开文件
QFile file(fileName[0]); //加载文件
//后续文件读写操作 同上
通过 selectedFiles
方法获取用户选择的文件路径列表,然后对这些文件进行相应的处理。
当然读取一个文件夹名称也可以通过帮助手册中的getOpenFileName
方法给出的示例进行修改程序参数即可。
//通过QFileDialog 弹窗选择txt文件 调用手册例程
QString fileName;
fileName = QFileDialog::getOpenFileName(this, tr("Open File"),
"E:/qtProject/00_QT_CLC/notebook2",
tr("Text (*.txt)"));
创建 / 保存文件
创建 / 保存文件相对于打开文件而言较为简单,通过调用 getSaveFileName
方法即可实现,通过修改帮助手册中的示例程序即可。
void Widget::on_pushButton3_clicked()
{
//通过文件选择框创建一个文件
QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"),
"E:/qtProject/00_QT_CLC/notebook2/data.txt",
tr("Text (*.txt)"));
//以文件流方式读写
//1. 打开创建的文件
QFile file(fileName);
if(!file.open(QIODevice::ReadWrite | QIODevice::Text| QIODevice::Truncate)) //打开文件
qDebug() << "file open error" << endl;
//将数据写入创建的文件中
QTextStream out(&file);
out.setCodec("UTF-8");
out << "write data from QFileDialog" << "\n";
file.close(); //关闭文件
}
四、记事本基本功能整合
因为涉及文件的打开,保存与关闭,因此需要将文件对象file和文件名fileName设置为类的成员属性,供成员函数的共同访问。
行编辑器与文本编辑器
行编辑器:
常用方法 | 功能 |
---|---|
setText(QString context) | 以覆盖的形式设置行编辑器内容 |
clear() | 清空行编辑器内容 |
QString text() | 获取行编辑器内容 |
文本编辑器:
与行编辑器不同的是,文本编辑器支持追加写数据,但没有text()方法,文本编辑器需要通过toPlainText
方法来获取编辑器的内容。
常用方法 | 功能 |
---|---|
setText(QString context) | 以覆盖的形式设置文本编辑器内容 |
append(QString context) | 以行的形式往文本编辑器中追加数据,行编辑器不能追加数据 |
clear() | 清空文本编辑器内容 |
QString toPlainText() | 获取文本编辑器内容 |
文件的打开
对于打开文件,只需将上述通过文件选择框选择文件 + 文件数据的读取 + 文本框显示内容三段代码进行合并即可,具体实现如下:
void Widget::on_pushButton1_clicked()
{
/******1. 通过QFileDialog 弹窗选择txt文件*****/
//通过QFileDialog 弹窗选择txt文件 调用手册例程
fileName = QFileDialog::getOpenFileName(this, tr("Open File"),
"E:/qtProject/00_QT_CLC/notebook2",
tr("Text (*.txt)"));
/*****2. 通过数据法读取txt文件内容*******/
//以文件流方式读写
//1. 打开文件
//可读可写打开文件
file.setFileName(fileName);
if(!file.open(QIODevice::ReadWrite| QIODevice::Text)) //打开文件
qDebug() << "file open error" << endl;
//读取文件数据流
QTextStream in(&file);
in.setCodec("UTF-8");
//在往文本编辑器中写数据前 先清空文本编辑器的数据 防止文件重复显示
ui->textEdit->clear();
while(!in.atEnd())
{
QString line = in.readLine();
//qDebug() << line << endl;
/******3. 将txt文件内容输出至文本编辑器******/
//3. 输出文件内容
//cout << line << endl;
ui->textEdit->append(line);
}
//关闭文件
//file.close(); //后续优化 在关闭按钮内关闭
}
注:
- 分步骤打开一个文件选项框代码冗余,但打开多个文件需要这样写
- 读取文件使用文件流较可靠,因为使用直接法会存在一些编码问题
文件的保存
同理,对于文件的保存与文件的打开类似,主要也分为三个步骤,读取文本编辑器数据 + 通过选择文件选项框创建文件 + 数据写入新文件进行保存。
//保存文件按钮
void Widget::on_pushButton3_clicked()
{
//文件未打开的情况 弹窗新建文件
if(!file.isOpen())
{
//通过文件选择框创建一个文件
QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"),
"E:/qtProject/00_QT_CLC/notebook2/data.txt",
tr("Text (*.txt)"));
//以文件流方式读写
//1. 打开创建的文件
file.setFileName(fileName);
//可读可写打开文件
if(!file.open(QIODevice::ReadWrite | QIODevice::Text| QIODevice::Truncate))
qDebug() << "file open error" << endl;
}
//清空原文件夹中的数据 存入文本编辑器数据
file.resize(0);
//将数据写入创建的文件中
QTextStream out(&file);
out.setCodec("UTF-8");
//获取文本编辑器内容
QString context = ui->textEdit->toPlainText();
out << context;
//关闭文件
//file.close(); //后续优化 在关闭按钮内关闭
}
关闭按钮的实现
在点击关闭按钮时需要通过isOpen
方法检测文件是否打开
//设计关闭按钮 用于关闭文件
void Widget::on_pushButton2_clicked()
{
ui->textEdit->clear();
if(file.isOpen())
{
file.close();
}
}
基础功能展示: