C++ QT零基础教学(二)

一. 引子

在上一篇文章里面稍微讲解了一点C++的基础,但是后面想了想要是还是继续单纯写C++的内容的话要写到几百年年以后了,都不知道什么时候到QT了,所以这次就直接开始从QT开始描写了,当然肯定不会是很有难度,尽量还是会用通俗易懂的语言,因为毕竟我也是才开始接触QT的新手

二. 新建工程

这里我们先举一个具体的例子,首先新建一个工程,点击NEW

选择Qt Widgets Application

设置一个工程的名字,和工程的存储位置

这里选择qmake

这里选择dialog.h,然后下一步

这里直接下一步

这里直接选择MinGW

直接完成

在这里生成了如下工程,工程创建成功后

三. QMessageBox

在Qt中,QMessageBox是一个用于显示对话框的类,它可以显示信息、警告、错误提示等,并提供按钮让用户进行交互

首先我们先点开上面二队dialog.ui,

点击一个Push Button放到这个页面上

然后右下角黄色框向下滑找到一个属性,text,PushButton,或者也可以直接更改按钮的名字

右键这个按钮,然后有一个选项转到槽,会出现如下一个界面

选择第一个clicked,然后点击ok,然后会在dialog.cpp的文件中生成一个函数,

然后我来解释一下这里发生了什么

在QT中,我们使用QT Designer设计界面,并为按钮等控件添加槽函数,这样当用户点击按钮时,就会自动调用我们编写的槽函数

QT自动生成了on_pushButton_Clicked()函数,QT在dialog.h中声明了这个槽函数,那么什么是槽函数,可以理解为QT里自动执行的函数,会在某些特定的时间发生时被QT触发

例如信号,类似于有人敲门(就是事件发生),槽函数就是听到敲门后,去开门(响应事件),在QT里,如果按钮被点击(信号发生),请自动执行某个函数(槽)

在转到槽的时候QT主要进行了以下操作,QT在我们的类中(这里是Dialog)加上了槽函数,例如,在dialog.h中自动添加了槽函数的声明,就像是在QT里登记了一个开门的人,但是这里还没有写开门后要干什么

cpp 复制代码
private slots:
    void on_pushButton_clicked();

然后在dialog.h中,自动创建一个空额槽函数,这里只是生成了这个函数,并不会自己调用它,必须等按钮被点击,QT才会自动执行这个函数

cpp 复制代码
void Dialog::on_pushButton_clicked()
{
    
}

QT在后台连接了"按钮被点击"和"槽函数",你在QT Designer里点击转到槽后,QT偷偷在后台连接了

1. QMessageBox information

现在在函数中写下如下代码,

cpp 复制代码
void Dialog::on_pushButton_clicked()
{
    QMessageBox::information(this,"information","是否退出系统",QMessageBox::Yes|QMessageBox::No,QMessageBox::Yes);
}

先执行一下这段代码看看效果,点击这个按钮后,就会跳出来这个界面

QMessageBox,主要用于提示用户信息,警告用户风险,显示错误信息,获取用户确认,而

QMessageBox::information()就是信息提示

· 这个函数的第一个参数this,this指向当前Dialog窗口的实例,可以这么简单理解,如果没有这个this,把函数的第一个参数换成NULL,那么之后这个提示用户信息窗口,就无法和Dialog产生关联,就无法通过窗口关闭最外面的界面

而如果加入了this参数就可以和外面的界面产生关联,能够关闭写入此界面的逻辑

那么后面的参数就很简单了,第一个information就是左上角的信息,然后是否退出系统就是中间给用户的提示信息,之后有一个|这个符号,表示下方有两个选择,一个是Yes、一个no,当然也可以有其他参数,只需要在后面再加入|这个然后写入想要的内容就可以了,以及最后一个参数就是默认没有选择时候的按钮,可以看到上面的图片有一个Yes处有一个高亮亮,所以如果把后面改成No的话,自然就是no的高亮

那下一步增加一点逻辑

cpp 复制代码
void Dialog::on_pushButton_clicked()
{
    QMessageBox::information(this,"information","是否退出系统",QMessageBox::Yes|QMessageBox::No,QMessageBox::Yes);
    close();
}

写入这段代码后,点击提示框后就可以点击yes和no关闭整个界面,可以发现是yes和no,但是如果我们要实现Yes是关闭,但是no就是不关闭怎么办,那就加入逻辑,因为这里的代码意思就是,点击这个按钮,弹出选项,但是不论选项什么

2. QMessageBox question

所以为了符合正常的逻辑,我们把代码再修改一下,这下实际的代码逻辑就非常的清晰了,能够点击yes就是关闭界面,不想关闭界面就直接点击no就可以了,但是其中代码的变动幅度也有点大,我们做一个解释

前面的那段代码,弹出QMessageBox::information() 信息框,但不是真正的询问。QMessageBox::Yes | QMessageBox::No 在 QMessageBox::information() 里不起作用,因为 information() 不会返回用户的选择,它只是显示一个对话框,让我们点击关闭按钮。

无论点什么,信息框都会直接关闭,程序执行 close(); 关闭窗口

而QMessageBox::question() 询问框:这个框有两个按钮:"是(Yes)"和"否(No)"

QMessageBox::question() 会返回我们的选择。ret == QMessageBox::Yes,执行 close(); 关闭窗口。ret == QMessageBox::No,close(); 不会执行,窗口保持打开状态

int ret这个步骤就是为了创建一个变量来存储QMessageBox::question()的返回值,存储这个返回值后之后使用if判断,所以就实现了我们最后的效果

cpp 复制代码
void Dialog::on_pushButton_clicked()
{
    int ret = QMessageBox::question(this,"information","是否退出系统",QMessageBox::Yes|QMessageBox::No,QMessageBox::Yes);
    if(ret == QMessageBox ::Yes)
    {
        close();
    }
}

那么接下来我们如法炮制,去控件里面添加第二个按钮

然后还是右键转到槽,自动帮我们生成按钮按下后的函数

3. QMessageBox warning

这是一个警告框,用来提示用户危险操作,就可以使用warning。上面的写法和下面的写入都可以

cpp 复制代码
void Dialog::on_pushButton_2_clicked()
{
    //QMessageBox::warning(this,"critical","此操作可能会对系统产生不可逆的影响",QMessageBox::Yes|QMessageBox::No,QMessageBox::Yes);
    QMessageBox::warning(this,"warning","这个操作可能导致数据丢失");
}

效果如下

如果注释掉第二行保留第一行则是,可以根据实际的开发中,决定warning的用处

4. QMessageBox critical

还是按照老方法,设置第三个按钮,

cpp 复制代码
void Dialog::on_pushButton_3_clicked()
{
    QMessageBox::critical(this,"错误","无法打开文件");
}

平时遇到的系统发生错误就用到的是这个功能

5. QMessageBox about

一般是用来显示关于软件的信息,例如还是创建第四个按钮,然后在函数中写入代码

cpp 复制代码
void Dialog::on_pushButton_4_clicked()
{
    QMessageBox::about(NULL,"about","默认提示框");
}

显示的参数如下,这里写入Null,只是提示一下前面的参数是可以更改的,并不是一直都需要写入this,同理后面的参数也都是可以改变的,包括加入Yes或者no等带有逻辑的参数

6. 汇总

QMessageBox的主要功能如上,这里写一段代码用于加深理解

使用第一种写法,这种写法我们自己手动创建了一个实例,QMessageBox messageBox(...) 先创建一个消息框对象 messageBox。messageBox.exec() 显式地执行这个对话框,并返回我们的选择。

exec()是QDialog继承的方法,它会阻塞当前函数,直到我们做出选择,exec()的返回值就是我们点击的按钮(QMessageBox::Yes 或 QMessageBox::No);

cpp 复制代码
 QMessageBox messageBox(QMessageBox::NoIcon,"登录","用户和密码验证是否成功?",QMessageBox::Yes|QMessageBox::No,NULL);

    messageBox.setText("验证完成,请确认是否继续");
   int result = messageBox.exec();

   switch(result)
    {
        case QMessageBox::Yes:
            QMessageBox::about(NULL,"提示","你好,你已经点击yes按钮");
            break;
        case QMessageBox::No:
            QMessageBox::about(NULL,"提示","你好,你已经点击no按钮");
            break;
        default:
            break;
    }

第二种写法,就是前面我们讲解的question的用法,这里将它具体与switch用发结合在了一起

cpp 复制代码
    int result = QMessageBox::question(this,"登录","用户和密码验证是否成功",QMessageBox::Yes|QMessageBox::No,QMessageBox::Yes);

    switch (result) {

        case QMessageBox::Yes:
            QMessageBox::about(this,"提示","你好,你已经点击yes按钮");
        break;
    case QMessageBox::No:
            QMessageBox::about(this,"提示","你好,你已经点击no按钮");
        break;
    default:
        break;

主要是这两种用法有什么区别,对于QMessageBox::question这种静态函数,就比较适用于我们现在的场景,因为只需要简单的使用yes和no按钮,所以在实际开发中也有个缺点没有扩展性

相较于自己创建实例的方法,缺点就是较为复杂,但是可以自定义QMessage的属性(比如设置按钮文本、窗口大小等),需要修改UI样式,就是加上额外的控件或者图标,或者需要队QMessage进行更复杂的控制

四. 面向对象编程

面向对象编程是C++这门语言的核心概念,上面我们也提到过实例或者类等概念,但对于刚开始接触的人确实不好理解,那么,今天就提到的类(class)和实例(instance,或称作Object)展开,进行一定的描述

1. 类(class)---模板或蓝图

类可以比作一个模板或者蓝图,定义了一类对象的属性和行为,我们举一个实际的例子。例如我们要设计一个Car(汽车)类,就应该有

属性(成员变量): 颜色、品牌 方法(成员函数):启动、加速、刹车,想象我们要造一辆机车,首先就需要一张蓝图,上面规定了颜色、品牌、加速方法等。类就是规则,但并不是实际存在

cpp 复制代码
// 汽车的设计蓝图(类)
class Car {
public:
    // 属性(数据)
    string color;
    string brand;

    // 方法(行为)
void accelerate() {
    std::cout << color << "" << brand << "加速!当前速度:" << ++speed << "km/h" << std::endl;
}

private:
    int speed = 0; // 私有属性,外部无法直接修改
};

2. 实例(Instance 对象Object)------具体的事物

实例是一个类的一个具体对象,每个对象都基于类的定义创建,并拥有自己的数据。例如上面的Car只是一个模板,但是宝马或者奔驰就是这个Car类的实例

根据蓝图创建出来的具体汽车就是实例。每辆汽车有独立的数据

cpp 复制代码
int main() {
    // 造两辆真实的汽车(实例)
    Car myCar;      // 我的红色宝马
    myCar.color = "红色";
    myCar.brand = "宝马";

    Car yourCar;    // 你的黑色奥迪
    yourCar.color = "黑色";
    yourCar.brand = "奥迪";

    myCar.accelerate();  // 我的车加速到1km/h
    yourCar.accelerate(); // 你的车也加速到1km/h(两辆车独立)
}

在这里benz和bmw都是car的实例,它们各自有自己的brand、color、speed,但是都是可以调用start和accelerate方法,目前的效果就如下

3. 构造函数(Constructor)---出厂设置

目前举例来说就是让汽出厂时自动设置颜色和品牌,避免忘记初始化。构建函数再对象创建时候自动调用,举个例子

现在我在里面加入了构造函数,在实例当中也可以像上面一样直接初始化,但是这里我们还是引入了构建函数

因为这样能够确保对象始终有效,直接初始化可能遗漏关键属性,导致对象处于"半完成"状态,加入构造函数会强制在创建时候提供必要参数

cpp 复制代码
#include <iostream>
// 汽车的设计蓝图(类)
class Car {
public:
    // 属性(数据)
    std::string color;
    std::string brand;

    Car(std::string c, std::string b)
    {
        color = c;
        brand = b;
        std::cout << "一辆" << color << brand << "已出厂!" << std::endl;
    }

    // 方法(行为)
    void accelerate() {
        std::cout << color << "" << brand << "加速!当前速度:" << ++speed << "km/h" << std::endl;
    }

private:
    int speed = 0; // 私有属性,外部无法直接修改
};

int main() {
    // 造两辆真实的汽车(实例)
    Car myCar("红色","宝马");      // 我的红色宝马
    
    Car yourCar("黑色","奥迪");    // 你的黑色奥迪

    myCar.accelerate();  // 我的车加速到1km/h
    yourCar.accelerate(); // 你的车也加速到1km/h(两辆车独立)
}

其次直接初始化需将成员设为public,破坏封装。构造函数可通过private变量隐藏实现的细节,例如

设置到了后面还可以直接执行计算,验证参数,连接数据库等

cpp 复制代码
class Car {
private:
    int speed; // 外部无法直接修改
public:
    Car(std::string c, std::string b, int s) : speed(s) { ... }
};

// 外部只能通过构造函数初始化speed,避免随意篡改
Car myCar("红色", "宝马", 0); 

另外一点就是构造函数的名字必须和类名相同,这是语法规定,这样编译器直到它是用来初始化对象的特殊函数,构建函数也没有返回值

Car是类名(设计蓝图),myCar是变量(实际的汽车),Car("红色","宝马"),我们写Car myCar()编译器自动调用Car(std::string,std::string)构造函数

构造函数旨在new对象时,调用你写 Car myCar("红色", "宝马");,编译器自动调用 Car(std::string, std::string) 构造函数。类 Car 只是模板,不会被创建实例,而 myCar 这个变量才是真正的对象。

如果使用普通函数就会重名。例如

cpp 复制代码
class Car {
public:
    void Car() {  //  这是普通函数,不是构造函数(没有返回值的构造函数才行)
        std::cout << "我是一个普通函数,不是构造函数!" << std::endl;
    }
};

构造函数也是唯一可以和类同名的函数,对于上面我们的实例代码都还可以换种写法,避免color = c这种重复赋值

cpp 复制代码
Car(std::string c, std::string b) : color(c), brand(b) {
    std::cout << "一辆" << color << brand << "已出厂!" << std::endl;
}

4. 继承(Inheritance)--升级车型

例如电动车继承汽车的基础功能(颜色、加速),同时增加新功能(充电)

cpp 复制代码
class ElectricCar : public Car {
public:
    // 调用父类构造函数初始化基础属性
    ElectricCar(std::string c, std::string b) : Car(c, b) {}

    // 新增功能
    void charge() {
        std::cout << "正在充电..." << std::endl;
    }
};



int main() {

    ElectricCar tesla("蓝色", "特斯拉");
    tesla.accelerate(); // 直接使用父类方法
    tesla.charge();    // 自己新增的方法
}

可以通过以上这个例子简单的理解继承的机制,ElectricCar 继承 了 Car ,扩展了它的功能,public Car 表示 ElectricCar 继承了 Car 的所有 public 和 protected 成员(但 private 的不继承)。ElectricCar 相当于 "电动车是汽车的一种"(继承就是一种"is-a"关系)

然后Car(c, b) 这里显式调用 Car 的构造函数,初始化 color 和 brand,这样 ElectricCar 继承 Car 的属性,不需要再手动赋值 color = c; 了

ElectricCar 没有自己定义 accelerate(),所以它直接使用 Car 的 accelerate() 方法。这是 ElectricCar 独有 的功能,Car 里没有 charge() 方法。 电动车有充电功能,但普通汽车没有,所以 charge() 只在 ElectricCar 里定义。

继承的作用就是让代码更简洁,ElectricCar 直接复用 Car 的功能,不用重复写 accelerate()、color、brand 等属性。最后的运行结果如下

那么这个章节博主的分享就到这里

相关推荐
knightkkzboy21 分钟前
《C语言中的“神秘符号”:转义字符的奥秘》
c语言·开发语言
妮妮学代码33 分钟前
c#:使用串口通讯实现数据的发送和接收
开发语言·c#
香油哥37 分钟前
QQuick3D-Model的实例化
qt·3d
饼干帅成渣1 小时前
我又又又又又又更新了~~纯手工编写C++画图,有注释~~~
开发语言·c++
Hello.Reader1 小时前
为什么选择 Rust 和 WebAssembly?
开发语言·rust·wasm
yinhezhanshen1 小时前
rust 的Clone
开发语言·rust
sunly_1 小时前
Flutter:签名板封装
开发语言·javascript·flutter
GISer_Jing1 小时前
设计模式分类解析与JavaScript实现
开发语言·javascript·设计模式
mit6.8242 小时前
[Sum] C++STL oj常用API
c++·算法·leetcode
Dongliner~2 小时前
【QT:控件】
开发语言·qt