C++ QT入门1——记事本基础功能实现(基本控件布局+信号与槽+文件类操作)

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;
} 

以图片作为按钮背景

资源文件:
  如果你的程序需要加载特定的资源(图标、文本翻译等),那么,将其放置在资源文件中,就再也不需要担心这些文件的丢失。

加载图片资源
C++ QT入门1——记事本基础功能实现(基本控件布局+信号与槽+文件类操作)_第1张图片 C++ QT入门1——记事本基础功能实现(基本控件布局+信号与槽+文件类操作)_第2张图片

设置窗口标题及图标
C++ QT入门1——记事本基础功能实现(基本控件布局+信号与槽+文件类操作)_第3张图片

添加图片
样式表->添加资源->border-image
C++ QT入门1——记事本基础功能实现(基本控件布局+信号与槽+文件类操作)_第4张图片 C++ QT入门1——记事本基础功能实现(基本控件布局+信号与槽+文件类操作)_第5张图片

将图片资源放入按钮三态
C++ QT入门1——记事本基础功能实现(基本控件布局+信号与槽+文件类操作)_第6张图片

二、按键响应——☆信号与槽

信号与槽基本概念

  在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)));

这段代码分别将信号 mysignalmysignalPara 连接到槽函数 myslotmyslotPara

4. 发射信号: 使用 emit 关键字发射信号。当信号被发射时,所有连接到这个信号的槽会被调用。

emit mysignal();    //发送信号
emit mysignalPara(123);    //发送信号

这将触发所有连接到这两个信号上的槽
自定义信号和槽是Qt编程中非常强大的特性,他们似的组件之间的通信变得灵活而松耦合。通过信号和槽,可以方便实现各种复杂的事件驱动逻辑。

自定义信号与槽的实际应用——页面的跳转

  对于从页面A跳转到页面B,只需要点击页面A上的按钮,隐藏页面A,打开页面B即可,而对于如何从页面B切换回页面A则可通过自定义信号和槽实现。
   页面B上的按钮点击后发射信号,页面A实时检测自定义信号,检测到到页面B中的按钮发出的自定义信号时,隐藏页面B,显示页面A。
思路流程图及伪代码实现:
C++ QT入门1——记事本基础功能实现(基本控件布局+信号与槽+文件类操作)_第7张图片
切换到新窗口:

    //页面切换
    //创建新窗口
    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设置为类的成员属性,供成员函数的共同访问。
C++ QT入门1——记事本基础功能实现(基本控件布局+信号与槽+文件类操作)_第8张图片

行编辑器与文本编辑器

行编辑器:

常用方法 功能
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();
    }
}

基础功能展示:
C++ QT入门1——记事本基础功能实现(基本控件布局+信号与槽+文件类操作)_第9张图片

你可能感兴趣的:(C++与Qt,c++,qt)