一. 引子
在上一篇文章里面稍微讲解了一点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 等属性。最后的运行结果如下
那么这个章节博主的分享就到这里