Qt学习笔记第51到60讲

发布于:2024-12-18 ⋅ 阅读:(88) ⋅ 点赞:(0)

第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组件

基本概念

      QComboBox Qt 框架中用于创建下拉列表的一个控件。它允许用户从一组选项中选择一个选项,并可以配置为可编辑,使用户能够在其中输入文本。QComboBox 提供了一系列方法来添加、删除和修改列表中的项,支持通过索引或文本检索项,并可以通过信号和槽机制来响应用户的选择变化。该控件广泛应用于需要从多个选项中进行选择的用户界面场景,例如表单和设置界面。

UI使用步骤

①拖动一个combo box控件到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++补充知识:模板

C++ 中,模板( Template )是一种通用的编程工具,允许程序员编写 泛型代码 ,使得 类或函数能够适 用于多种不同的数据类型 而不需要重复编写相似的代码。 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容器简介

 在 Qt 框架中, QList 是一个容器类,它在内部实现上类似于一个数组,但也提供了一些链表的特性。QList 的设计旨在提供一个在多数情况下既高效又方便的通用列表容器。用于存储元素列表。它提供了丰富的功能,包括添加、移除、访问元素等。

QList内部工作原理

使用场景

①当你需要快速的随机访问(如通过索引访问元素)时, QList 是一个不错的选择。
②如果你的主要操作是在列表的两端添加或移除元素, QList 也表现得很好。

基本用法

包含头文件 :首先,你需要包含 QList 的头文件。
#include <QList>
创建 QList 实例 :创建一个 QList 对象,并指定存储的元素类型。
QList<int> list;
添加元素 :使用 append push_back 方法添加元素。
list.append(1);
list.append(2);
list.append(3);
访问元素 :可以使用下标操作符或 at() 方法访问元素。
int firstElement = list[0];
int secondElement = list.at(1);
遍历列表 :使用迭代器或范围基的 for 循环遍历列表。
for(int i = 0; i < list.size(); ++i) { // size = sizeof(arr)/sizeof(arr[0])
qDebug() << list[i];
}
// 或者使用范围基的 for 循环
for(int item : list) {
qDebug() << item;
}
再写一个例子复习一下范围for循环的用法:
#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(); // 清空整个列表