第51讲 记事本实现打开功能
回到第24个功能文件Notepad,给UI中的各个控件添加槽函数。
①开始按钮
void Widget::on_btnOpen_clicked()
{
QString fileName=QFileDialog::getOpenFileName(this,tr("Open File"),
"E:\\6_Qt Projects\\24_Notepad\\files",tr("Text(*.txt *.doc)"));
//QFileDialog限制程序可打开的文件形式为txt文件或者doc文本
ui->textEdit->clear();
//每次打开文件时清除控件区域“textEdit”
QFile file;
file.setFileName(fileName);
if(!file.open(QIODevice::ReadOnly|QIODevice::Text))
{
qDebug()<<"file open error";
}
QTextStream in(&file);
in.setCodec("UTF-8");
while(!in.atEnd())
{
QString context=in.readLine();
//qDebug()<<qPrintable(context);
ui->textEdit->append(context);
//将读取到的每行内容通过 append 方法添加到界面的文本编辑框(ui->textEdit)中
}
file.close();
}
实现效果:
依次点击
输出结果为:
再试一下打开另外一个文件:
新内容正确显示,原本的文件内容也已经被删除。
第52讲 记事本实现保存新建文件的功能
本质山是为下面这个按键编写槽函数。
代码示例
void Widget::on_btnSave_clicked()
{
QString fileName=QFileDialog::getSaveFileName(this,tr("Save File"),
"E:\\6_Qt Projects\\24_Notepad\\files\\untitle.txt",tr("Text(*.txt *.doc)"));
QFile file;
file.setFileName(fileName);
if(!(file.open(QIODevice::WriteOnly|QIODevice::Text)))
{
qDebug()<<"failed to open file!"<<endl;
}else{
QTextStream out(&file);
out.setCodec("UTF-8");
QString context=ui->textEdit->toPlainText();
out<<context;
}
file.close();
}
逐步讲解
1.获取保存文件名
QString fileName=QFileDialog::getSaveFileName(this,tr("Save File"),
"E:\\6_Qt Projects\\24_Notepad\\files\\untitle.txt",tr("Text(*.txt *.doc)"));
2.设置文件对象的文件名
QFile file;
file.setFileName(fileName);
3.打开文件并进行错误处理
if(!(file.open(QIODevice::WriteOnly|QIODevice::Text)))
{
qDebug()<<"failed to open file!"<<endl;
}
4.写入文件
else{
QTextStream out(&file);
out.setCodec("UTF-8");
QString context=ui->textEdit->toPlainText();
out<<context;
}
5.关闭文件
第53讲 记事本实现关闭按键
在widget.h文件中,实例化一个QFile类型的对象file,作为widget类的public成员变量。
public:
QFile file;
Widget(QWidget *parent = nullptr);
~Widget();
在为关闭键编写槽函数之前,我们需要先修改打开和保存的槽函数:
void Widget::on_btnOpen_clicked()
{
QString fileName=QFileDialog::getOpenFileName(this,tr("Open File"),
"E:\\6_Qt Projects\\24_Notepad\\files",tr("Text(*.txt *.doc)"));
//QFileDialog限制程序可打开的文件形式为txt文件或者doc文本
ui->textEdit->clear();
//每次打开文件时清除控件区域“textEdit”
//QFile file;
file.setFileName(fileName);
if(!file.open(QIODevice::ReadOnly|QIODevice::Text))
{
qDebug()<<"file open error";
}
QTextStream in(&file);
in.setCodec("UTF-8");
while(!in.atEnd())
{
QString context=in.readLine();
//qDebug()<<qPrintable(context);
ui->textEdit->append(context);
//将读取到的每行内容通过 append 方法添加到界面的文本编辑框(ui->textEdit)中
}
//file.close();
}
void Widget::on_btnSave_clicked()
{
QString fileName=QFileDialog::getSaveFileName(this,tr("Save File"),
"E:\\6_Qt Projects\\24_Notepad\\files\\untitle.txt",tr("Text(*.txt *.doc)"));
//QFile file;
file.setFileName(fileName);
if(!(file.open(QIODevice::WriteOnly|QIODevice::Text)))
{
qDebug()<<"failed to open file!"<<endl;
}else{
QTextStream out(&file);
out.setCodec("UTF-8");
QString context=ui->textEdit->toPlainText();
out<<context;
}
//file.close();
}
将file的重定义与close成员函数注释掉。
最后展示一下关闭键的槽函数示例:
void Widget::on_btnClose_clicked()
{
if(file.isOpen())
{
file.close();
ui->textEdit->clear();
}
}
第54讲 字符编码问题引入
在工程文件中新建一个ANSI编码的文本:
①可以直接通过另存为修改编码格式
②微软的记事本有这个选项卡可以修改编码格式
我在里面写了一句唐诗,再通过我们自己的记事本打开可以发现读取出来的文本完全是乱码。
这是因为我们在这里直接写死了编码形式为UTF-8。
第55讲 QComboBox组件
基本概念

UI使用步骤

②右键选中编辑项目
③编辑组合框,添加想要的内容
④按下ctrl+r运行之后查看
编程步骤
①查阅手册,索引QcomboBox适用的信号
②查看void currentIndexChanged(int index)详细定义
③按下ctrl+b对工程进行整体构建,然后在Widget的构造函数内部使用内联函数connect建立信号与槽的链接
Widget::Widget(QWidget *parent): QWidget(parent) , ui(new Ui::Widget)
{
ui->setupUi(this);
connect(ui->comboBox,SIGNAL(currentIndexChanged(int)),
this,SLOT(oncurrentIndexChanged(int)));
}
注意点:
④在头文件中widget的类内部声明槽函数
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
public slots:
void oncurrentIndexChanged(int index); //公有槽函数的声明
private:
Ui::Widget *ui;
};
⑤在widget.cpp文件内对槽函数进行实现
void Widget::oncurrentIndexChanged(int index)
{
qDebug()<<index;
qDebug()<<ui->comboBox->currentText();
}
运行测试
如上图所示,槽函数已经正常输出调试信息。
第56讲 记事本优化打开各种编码类型的文件
①将原来的label(“UTF-8”)替换为comboBox,像上一讲一样添加“UTF-8”、“UTF-16”等类目;
然后回到代码编辑界面按下ctrl+b构建整个工程后不要忘记在Widget的构造函数内部使用内联函数connect建立信号与槽的链接;之后按照之前的代码风格,在widget.h里面声明槽函数:
#ifndef WIDGET_H
#define WIDGET_H
#include <QFile>
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
QFile file;
Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void on_btnOpen_clicked();
void on_btnSave_clicked();
void on_btnClose_clicked();
void oncurrentIndexChanged(int index);//声明参函数
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
最后在widget.cpp文件中实现代码。
②注意到显示乱码的根源是因为我们在打开按键、保存按键的槽函数中各自实例化了一个QTextStream类型的对象,并且借助其中的成员函数setCodec将文件的编码类型限制为“UTF-8”。为了解决这个问题,我们可以利用comboBox选择编码类型文本,然后将其以字符串的形式传递给程序处理,同时comboBox类内存在一个接口currentText恰好可以传递控件当前显示文本。
注意到setCodec需要传入的形参类型为const char*:
void setCodec(const char *codecName);
而currentText的返回值类型为QString:
QString currentText() const;
因此我们需要对这两者进行数据转换:
QTextStream in(&file);
QString str=ui->comboBox->currentText();
const char* c_str=str.toStdString().c_str();
in.setCodec(c_str);
整体代码widget.cpp:
#include "widget.h"
#include "ui_widget.h"
#include <QFileDialog>
#include <QDebug>
Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget)
{
ui->setupUi(this);
this->setLayout(ui->verticalLayout);
ui->widgetBottom->setLayout(ui->horizontalLayout);
connect(ui->comboBox,SIGNAL(currentIndexChanged(int)),
this,SLOT(oncurrentIndexChanged(int)));
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_btnOpen_clicked()
{
QString fileName=QFileDialog::getOpenFileName(this,tr("Open File"),
"E:\\6_Qt Projects\\24_Notepad\\files",tr("Text(*.txt *.doc)"));
//QFileDialog限制程序可打开的文件形式为txt文件或者doc文本
ui->textEdit->clear();
//每次打开文件时清除控件区域“textEdit”
//QFile file;
file.setFileName(fileName);
if(!file.open(QIODevice::ReadOnly|QIODevice::Text))
{
qDebug()<<"file open error";
}
QTextStream in(&file);
QString str=ui->comboBox->currentText();
const char* c_str=str.toStdString().c_str();
in.setCodec(c_str);
while(!in.atEnd())
{
QString context=in.readLine();
//qDebug()<<qPrintable(context);
ui->textEdit->append(context);
//将读取到的每行内容通过 append 方法添加到界面的文本编辑框(ui->textEdit)中
}
//file.close();
}
void Widget::on_btnSave_clicked()
{
QString fileName=QFileDialog::getSaveFileName(this,tr("Save File"),
"E:\\6_Qt Projects\\24_Notepad\\files\\untitle.txt",tr("Text(*.txt *.doc)"));
//QFile file;
file.setFileName(fileName);
if(!(file.open(QIODevice::WriteOnly|QIODevice::Text)))
{
qDebug()<<"failed to open file!"<<endl;
}else{
QTextStream out(&file);
QString str=ui->comboBox->currentText();
const char* c_str=str.toStdString().c_str();
out.setCodec(c_str);
QString context=ui->textEdit->toPlainText();
out<<context;
}
//file.close();
}
void Widget::on_btnClose_clicked()
{
if(file.isOpen())
{
file.close();
ui->textEdit->clear();
}
}
void Widget::oncurrentIndexChanged(int index)
{
ui->comboBox->currentText();
}
③此时面对乱码文件已经有了解决办法,但更多时候我们希望在QComboBox空间上对编码类型做出调整后,乱码被自动修复,因此我们需要对这个槽函数作出修改:
void Widget::oncurrentIndexChanged(void)
{
ui->textEdit->clear();
if(file.isOpen())
{
QTextStream in(&file);
in.setCodec(ui->comboBox->currentText().toStdString().c_str());
//链式调用访问成员变量
file.seek(0);//将光标移动回起始点
while(!in.atEnd())
{
QString context=in.readLine();
ui->textEdit->append(context);
}
}
}
注意我这里因为不用打印index调试了所以将函数的形参也一并修改为void了。
第57讲 记事本支持光标行列值显示
显示光标所在位置需要使用QTextEdit内部的信号函数 void cursorPositionChanged()。
所以我们需要先在widget构造函数当中使得该函数与它的槽函数相关联,再在外部实现槽函数。
外部实现槽函数的重点是使用QTextCursor类内部的blockNumber方法与columnNumber方法。
槽函数示例:
void Widget::onCursorPositionChanged(void)
{
QTextCursor cursor=ui->textEdit->textCursor();
qDebug()<<cursor.blockNumber()+1 <<cursor.columnNumber()+1;
QString blockNum=QString::number(cursor.blockNumber()+1);
QString columnNum=QString::number(cursor.columnNumber()+1);
const QString labelMes="L:"+blockNum+" "+"C:"+columnNum;
ui->labelPositon->setText(labelMes);
}
输出结果:
不过,通过setText方法传输的拼接字符串中不能包含中文,否则会输出乱码。
解决方案是先关闭QT软件,随后打开工具->选项,
随后对文本编辑器中的默认编码进行修改,修改为“UTF-8”。
整体代码(头文件只需要在自动生成的基础上声明槽函数即可,省略):
#include "widget.h"
#include "ui_widget.h"
#include <QFileDialog>
#include <QDebug>
Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget)
{
ui->setupUi(this);
this->setLayout(ui->verticalLayout);
ui->widgetBottom->setLayout(ui->horizontalLayout);
connect(ui->comboBox,SIGNAL(currentIndexChanged(int)),
this,SLOT(onCurrentIndexChanged(void)));
connect(ui->textEdit,SIGNAL(cursorPositionChanged()),
this,SLOT(onCursorPositionChanged()));
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_btnOpen_clicked()
{
QString fileName=QFileDialog::getOpenFileName(this,tr("Open File"),
"E:\\6_Qt Projects\\31_NotepadCodec\\files",tr("Text(*.txt *.doc)"));
//QFileDialog限制程序可打开的文件形式为txt文件或者doc文本
ui->textEdit->clear();
//每次打开文件时清除控件区域“textEdit”
file.setFileName(fileName);
if(!file.open(QIODevice::ReadOnly|QIODevice::Text))
{
qDebug()<<"file open error";
}
QTextStream in(&file);
QString str=ui->comboBox->currentText();
const char* c_str=str.toStdString().c_str();
in.setCodec(c_str);
while(!in.atEnd())
{
QString context=in.readLine();
//qDebug()<<qPrintable(context);
ui->textEdit->append(context);
//将读取到的每行内容通过 append 方法添加到界面的文本编辑框(ui->textEdit)中
}
//file.close();
}
void Widget::on_btnSave_clicked()
{
QString fileName=QFileDialog::getSaveFileName(this,tr("Save File"),
"E:\\6_Qt Projects\\31_NotepadCodec\\files\\untitle.txt",tr("Text(*.txt *.doc)"));
//QFile file;
file.setFileName(fileName);
if(!(file.open(QIODevice::WriteOnly|QIODevice::Text)))
{
qDebug()<<"failed to open file!"<<endl;
}else{
QTextStream out(&file);
QString str=ui->comboBox->currentText();
const char* c_str=str.toStdString().c_str();
out.setCodec(c_str);
QString context=ui->textEdit->toPlainText();
out<<context;
}
//file.close();
}
void Widget::on_btnClose_clicked()
{
if(file.isOpen())
{
file.close();
ui->textEdit->clear();
}
}
void Widget::onCurrentIndexChanged(void)
{
ui->textEdit->clear();
if(file.isOpen())
{
QTextStream in(&file);
in.setCodec(ui->comboBox->currentText().toStdString().c_str());
//链式调用访问成员变量
file.seek(0);//将光标移动回起始点
while(!in.atEnd())
{
QString context=in.readLine();
ui->textEdit->append(context);
}
}
}
void Widget::onCursorPositionChanged(void)
{
QTextCursor cursor=ui->textEdit->textCursor();
qDebug()<<cursor.blockNumber()+1 <<cursor.columnNumber()+1;
QString blockNum=QString::number(cursor.blockNumber()+1);
QString columnNum=QString::number(cursor.columnNumber()+1);
const QString labelMes="L:"+blockNum+" "+"C:"+columnNum;
ui->labelPositon->setText(labelMes);
}
第58讲 记事本添加打开文件的提示
使用QWidget内部的setWindowTitle方法实现需求
示例:
this->setWindowTitle("NotesBook");
槽函数的各自改动:
#include "widget.h"
#include "ui_widget.h"
#include <QFileDialog>
#include <QDebug>
Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget)
{
ui->setupUi(this);
this->setLayout(ui->verticalLayout);
ui->widgetBottom->setLayout(ui->horizontalLayout);
connect(ui->comboBox,SIGNAL(currentIndexChanged(int)),
this,SLOT(onCurrentIndexChanged(void)));
connect(ui->textEdit,SIGNAL(cursorPositionChanged()),
this,SLOT(onCursorPositionChanged()));
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_btnOpen_clicked()
{
QString fileName=QFileDialog::getOpenFileName(this,tr("Open File"),
"E:\\6_Qt Projects\\31_NotepadCodec\\files",tr("Text(*.txt *.doc)"));
//QFileDialog限制程序可打开的文件形式为txt文件或者doc文本
ui->textEdit->clear();
//每次打开文件时清除控件区域“textEdit”
file.setFileName(fileName);
if(!file.open(QIODevice::ReadOnly|QIODevice::Text))
{
qDebug()<<"file open error";
}
this->setWindowTitle(fileName+"-NotesBook");
QTextStream in(&file);
QString str=ui->comboBox->currentText();
const char* c_str=str.toStdString().c_str();
in.setCodec(c_str);
while(!in.atEnd())
{
QString context=in.readLine();
//qDebug()<<qPrintable(context);
ui->textEdit->append(context);
//将读取到的每行内容通过 append 方法添加到界面的文本编辑框(ui->textEdit)中
}
//file.close();
}
void Widget::on_btnSave_clicked()
{
QString fileName=QFileDialog::getSaveFileName(this,tr("Save File"),
"E:\\6_Qt Projects\\31_NotepadCodec\\files\\untitle.txt",tr("Text(*.txt *.doc)"));
//QFile file;
file.setFileName(fileName);
if(!(file.open(QIODevice::WriteOnly|QIODevice::Text)))
{
qDebug()<<"failed to open file!"<<endl;
}else{
this->setWindowTitle(fileName+"-NotesBook");
QTextStream out(&file);
QString str=ui->comboBox->currentText();
const char* c_str=str.toStdString().c_str();
out.setCodec(c_str);
QString context=ui->textEdit->toPlainText();
out<<context;
}
//file.close();
}
void Widget::on_btnClose_clicked()
{
if(file.isOpen())
{
file.close();
ui->textEdit->clear();
this->setWindowTitle("NotesBook");
}
}
void Widget::onCurrentIndexChanged(void)
{
ui->textEdit->clear();
if(file.isOpen())
{
QTextStream in(&file);
in.setCodec(ui->comboBox->currentText().toStdString().c_str());
//链式调用访问成员变量
file.seek(0);//将光标移动回起始点
while(!in.atEnd())
{
QString context=in.readLine();
ui->textEdit->append(context);
}
}
}
void Widget::onCursorPositionChanged(void)
{
QTextCursor cursor=ui->textEdit->textCursor();
//qDebug()<<cursor.blockNumber()+1 <<cursor.columnNumber()+1;
QString blockNum=QString::number(cursor.blockNumber()+1);
QString columnNum=QString::number(cursor.columnNumber()+1);
const QString labelMes="L:"+blockNum+" "+"C:"+columnNum;
ui->labelPositon->setText(labelMes);
}
第59讲 C++补充知识:模板
类模板
语法介绍
template <typename 名称>
class PrintEverything{
private:
名称 data;
public:
void printfEverything()
{
cout<<"data = "<<data<<endl;
}
void setEverything(T data)
{
this->data=data;
}
};
注意点
含有模板的类无法直接实例化出对象:
正确演示:
#include <iostream>
using namespace std;
template <typename T>
class PrintEverything{
private:
T data;
public:
void printfEverything()
{
cout<<"data = "<<data<<endl;
}
void setEverything(T data)
{
this->data=data;
}
};
int main()
{
PrintEverything <int> p1;
p1.setEverything(100);
p1.printfEverything();
PrintEverything <string> p2;
p2.setEverything("Hello World");
p2.printfEverything();
return 0;
}
运行结果
函数模板
将函数的返回类型设置为模板,可以让函数出咯不同类型的数据(效果类似于函数重载)。
#include <iostream>
using namespace std;
template <typename T>
T add(T a,T b)
{
return a + b;
}
int main(void)
{
int ret1 = add(4,10);
double ret2 = add(3.14, 6.28);
cout << "ret1 = "<< ret1<<endl;
cout << "ret2 = " << ret2 << endl;
system("pause");
return 0;
}
第60讲 QList容器简介
QList内部工作原理
使用场景
基本用法
#include <QList>
QList<int> list;
list.append(1);
list.append(2);
list.append(3);
int firstElement = list[0];
int secondElement = list.at(1);
for(int i = 0; i < list.size(); ++i) { // size = sizeof(arr)/sizeof(arr[0])
qDebug() << list[i];
}
// 或者使用范围基的 for 循环
for(int item : list) {
qDebug() << item;
}
#include <iostream>
using namespace std;
int main()
{
int arr[10] = {0,1,2,3,4,5,6,7,8,9};
for (int element :arr)
{
cout << element<<" ";
}
system("pause");
return 0;
}
移除元素:使用 removeAt 、 removeOne 或 clear 方法移除元素。
list.removeAt(1); // 移除索引为 1 的元素
list.removeOne(3); // 移除一个值为 3 的元素
list.clear(); // 清空整个列表