当前位置: 首页 > article >正文

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里登记了一个开门的人,但是这里还没有写开门后要干什么

private slots:
    void on_pushButton_clicked();

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

void Dialog::on_pushButton_clicked()
{
    
}

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

1. QMessageBox information

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

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的高亮

        那下一步增加一点逻辑

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判断,所以就实现了我们最后的效果

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。上面的写法和下面的写入都可以 

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

        效果如下

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

4. QMessageBox critical

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

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

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

5. QMessageBox about

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


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

 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用发结合在了一起

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

// 汽车的设计蓝图(类)
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类的实例
        根据蓝图创建出来的具体汽车就是实例。每辆汽车有独立的数据

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)—出厂设置

        目前举例来说就是让汽出厂时自动设置颜色和品牌,避免忘记初始化。构建函数再对象创建时候自动调用,举个例子
        现在我在里面加入了构造函数,在实例当中也可以像上面一样直接初始化,但是这里我们还是引入了构建函数
        因为这样能够确保对象始终有效,直接初始化可能遗漏关键属性,导致对象处于“半完成”状态,加入构造函数会强制在创建时候提供必要参数

#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变量隐藏实现的细节,例如
        设置到了后面还可以直接执行计算,验证参数,连接数据库等

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 这个变量才是真正的对象。

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

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

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

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

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

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

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 等属性。最后的运行结果如下

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


http://www.kler.cn/a/589202.html

相关文章:

  • tomato靶场通关攻略
  • Leetcode-131.Palindrome Partitioning [C++][Java]
  • ST的全新STM32U3微控制器(MCU)简析
  • 视频推拉流EasyDSS案例分析:互联网直播/点播技术与平台创新应用
  • 【PyTorch教学】pytorch 基本语法
  • 消息队列 Kafka、RocketMQ、RabbitMQ 对比与分析
  • 蓝桥杯备考:图论之Prim算法
  • 分类操作-06.根据id删除分类
  • python 打印阳历对应的日农历时间
  • .gitignore 文件用于 Git 应忽略的文件夹的格式
  • SNX币合规交易突破 XBIT去中心化交易所引领DEX安全新范式
  • Notepad++插件:快捷选择成对括号之间的内容
  • 【SpringMVC】常用注解:@RequestHeader
  • WEB UI自动化测试中,元素定位的八大定位方式详解
  • docker,centos容器开机启动程序
  • 手搓排列型枚举递归搜索树 全排列问题(dfs)
  • 南邮大一统计学想转码,考研还是就业?如何避免就业被卡?转专业难度大吗?是CC++或Java?
  • 深度学习多模态人脸情绪识别:从理论到实践
  • 提升fcp
  • “Ubuntu禁止root用户通过SSH直接登录”问题的解决