声明
该笔记的内容摘自于传智播客B站视频:https://www.bilibili.com/video/BV1g4411H78N?p=17,只用作自己的学习与分享,侵删。
另外,视频质量很好,推荐一波~
1 Qt概述
1.1 什么是Qt
Qt是一个跨平台的C++图形用户界面应用程序框架。它为应用程序开发者提供建立艺术级图形界面所需的所有功能。它是完全面向对象的,很容易扩展,并且允许真正的组件编程。
1.2 Qt的优点
- 跨平台,几乎支持所有平台
- 接口简单,容易上手,学习Qt框架对学习其它框架有参考意义
- 一定程度上简化了内存回收机制
- 开放效率高,能够快速的构建应用程序
- 有很好的社区氛围,市场份额在缓慢上升
- 可以进行嵌入式开发
1.3 成功案例
- Linux 桌面环境 KDE
- WPS Office 办公软件
- Google Earth 谷歌地图
- VirtualBox 虚拟机软件
- …
2 创建Qt程序
2.1 创建选项
常用默认选项Application:创建一个桌面Qt应用,包含一个基于Qt设计师的主窗体。预选一个可用的Qt桌面版本用于编译程序。
注意:创建的文件名称不能有中文可以有下划线,路径不能出现中文。
三大基类:QWidget是QMainWindow和QDialog的父类(继承),QWdiget是空的,QMainWindow多了状态栏,QDialog和对话框有关。
创建界面(Generate form)如果选✔会多出来ui文件,可直接拖拽框架使用。
2.2 main.cpp
#include "mywidget.h"
#include <QApplication>// 包含一个应用程序类的头文件
//main程序入口 argc命令行变量的数量 argv命令行变量的数组
int main(int argc, char *argv[])
{
//a应用程序对象,在Qt中,应用程序对象 有且仅有一个
QApplication a(argc, argv);
//窗口对象 myWidget父类 -> QWidget
myWidget w;
//窗口对象 默认不会显示,必须要调用show方法显示窗口
w.show();
//让应用程序对象进入消息循环
//当代码阻塞到这行
return a.exec();
//保证窗口的持续存在,防止一闪而过,直到点击右上角的叉关闭
}
2.3 widget.h
#ifndef WIDGET_H//#ifndef是"if not defined"的简写
#define WIDGET_H//防止头文件重复包含
#include <QWidget> //包含头文件 QWidget 窗口类
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT // Q_OBJECT宏,允许类中使用信号和槽的机制
public:
Widget(QWidget *parent = nullptr); //构造函数
~Widget(); //析构函数
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
2.4 命名规范
- 类名 首字母大写,单词和单词之间首字母大写
- 函数名 变量名称 首字母小写,单词和单词之间首字母大写
2.5 快捷键
- 注释 ctrl + /
- 运行 ctrl + r
- 编译 ctrl + b
- 字体缩放 ctrl + 鼠标滚轮
- 查找 ctrl + f
- 整行移动 ctrl + shift + ↑ 或者↓
- 帮助文档 F1
- 自动对齐 ctrl + i;
- 同名之间的.h 和 .cpp切换 F4
2.6 帮助文档用法
按F1快捷键可找到帮助文档,或者去安装目录里找bin文件,其中也有帮助文档。
主要用于了解类的基本信息:
下移,找类的使用方法,点进去即可看到详情
2.6 创建第一个PushButoon
widget.cpp文件
#include "widget.h"
#include <QPushButton>
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
//创建一个按钮
QPushButton * btn = new QPushButton;
//在帮助文档中可找到父类为QWidget,窗口默认不显示,需要用show方法
//btn->show(); 直接使用,会出现单独的窗口,而不是出现在主窗口之内
//需求:让btn对象 依赖在 widget窗口中
btn->setParent(this);
//显示文本
btn->setText("第一个按钮");
//创建第二个按钮 按照控件的大小创建窗口
QPushButton * btn2 = new QPushButton;
btn2->setParent(this);
btn2->setText("第二个按钮");
//移动btn2的位置
btn2->move(150,20);//x,y坐标
//改变btn2的大小
btn2->resize(100,70);
//重置窗口大小
resize(220,110);//resize(int w,int h):宽,高
//设置固定的窗口的大小,使得用户无法用鼠标拖拽窗口大小
setFixedSize(600,400);
//设置窗口标题
setWindowTitle("第一个窗口");
}
Widget::~Widget()
{
}
注意:文字不能正常显示的情况,菜单栏找到工具->选项->文本编辑器->行为-> 默认编码,设置为UTF-8即可。
效果展示
3 对象树
- 当创建的对象在堆区的时候,如果指定的父亲是QObject派生下来的类或者其子类派生下来的类,可以不用管释放操作,对象会被放入对象树种
- 一定程度上简化了内存回收机制。
总的来说:一个parent被析构时,会自动delete/析构它所有的children;一个children被析构时,会自动从他的parent中移除。
4 坐标系
- Qt坐标系的原点在左上角,X向右增大,Y向下增大;
- 对于嵌套窗口,其坐标是相对父窗口来说的。
5 信号和槽 *
5.1 样例
信号的英文单词为signal,槽的单词为slot,可以去帮助文档中找相关信息,如果找不到可以去父类中查找。
这里是PushButoon的相关信号:
用connect函数连接信号和槽
widget.cpp代码如下:
#include "widget.h"
#include <QPushButton>
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
QPushButton * myBtn = new QPushButton;
myBtn->setText("关闭按钮");
myBtn->setParent(this);
//需求 点关闭按钮 关闭窗口
//参数1 信号的发生者 参数2 发送的信号(函数的地址) 参数3 信号的接收者 参数4 处理的槽函数
connect(myBtn,&QPushButton::clicked,this,&Widget::close);
}
Widget::~Widget()
{
}
点击按钮即可关闭窗口
5.2 自定义的信号和槽
建立一个老师类和学生类,自定义信号和槽
首先右键项目 -> Add New… -> 选C++ Class类 -> Base Class 选择 QObject 确认
student.h
#ifndef STUDENT_H
#define STUDENT_H
#include <QObject>
class Student : public QObject
{
Q_OBJECT
public:
explicit Student(QObject *parent = nullptr);
signals:
public slots:
//槽函数,早期Qt必须写在public slots下面,高级版本可以写到public或全局下
//返回值 void , 需要声明, 也需要实现, 可以有参数, 可以发生重载
void Hello();
};
#endif // STUDENT_H
teacher.h
#ifndef TEACHER_H
#define TEACHER_H
#include <QObject>
class Teacher : public QObject
{
Q_OBJECT
public:
explicit Teacher(QObject *parent = nullptr);
signals:
//自定义信号写到signals下面
//规定:返回值是void,只需要声明,不需要实现
//可以有参数,可以重载
void StandUp();
};
#endif // TEACHER_H
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <teacher.h>
#include <student.h>
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
Teacher *ls;
Student *xs;
void ClassStart();
};
#endif // WIDGET_H
student.cpp
#include "student.h"
#include <QDebug>
Student::Student(QObject *parent) : QObject(parent)
{
}
void Student::Hello()
{
//调试信息直接输出到控制台上
qDebug()<<"老师好!";
}
widget.cpp
#include "widget.h"
#include <QPushButton>
//Teacher 类 老师类
//Student 类 学生类
//上课铃响 老师会触发一个信号,说起立,学生响应信号,回应老师好
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
//创建一个老师d对象
this->ls = new Teacher(this);
//创建一个学生对象
this->xs = new Student(this);
//上课铃响,老师喊起立,学生回应老师好
connect(ls,&Teacher::StandUp,xs,&Student::Hello);
ClassStart();//注意代码顺序,先连接,再触发才有效
}
void Widget::ClassStart()
{
//上课,调用后, 老师触发信号
emit ls->StandUp();
}
Widget::~Widget()
{
}
构建程序的效果:
5.3 信号和槽重载的解决
在上述程序的基础上,Teacher类的头文件中加上声明 void StandUp(QString sentence); Stuendent类的头文件中加上声明 void Hello(QString sentence),并在cpp文件中写好函数体 qDebug()<<“老师”<<sentence;
更新widget.cpp文件如下:
#include "widget.h"
#include <QPushButton>
//Teacher 类 老师类
//Student 类 学生类
//上课铃响 老师会触发一个信号,说起立,学生响应信号,回应老师好
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
//创建一个老师d对象
this->ls = new Teacher(this);
//创建一个学生对象
this->xs = new Student(this);
//上课铃响,老师喊起立,学生回应
//重载后,编译器不知道选用哪一个函数
//connect(ls,&Teacher::StandUp,xs,&Student::Hello);发生错误
//正确方法:函数指针 -> 函数地址
//声明成员函数的地址,要在前面加上作用域
void(Teacher::*tearcherSignal)(QString) = &Teacher::StandUp;
void(Student::*studentSlot)(QString) = &Student::Hello;
connect(ls,tearcherSignal,xs,studentSlot);
ClassStart();//注意代码顺序,先连接,再触发才有效
}
void Widget::ClassStart()
{
//上课,调用后, 老师触发信号
emit ls->StandUp("威武霸气!");
}
Widget::~Widget()
{
}
效果如下:
可以看到上述输出sentence的文字,本没有加引号,但是输出了引号。将对应输出改成如下格式即可消去引号。
5.4 信号连接信号
5.4.1 代码样例
#include "widget.h"
#include <QPushButton>
//Teacher 类 老师类
//Student 类 学生类
//上课铃响 老师会触发一个信号,说起立,学生响应信号,回应老师好
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
//创建一个老师d对象
this->ls = new Teacher(this);
//创建一个学生对象
this->xs = new Student(this);
//上课铃响,老师喊起立,学生回应
//点击按钮触发老师的行为
QPushButton *btn = new QPushButton("上课",this);
//重置窗口大小
resize(100,100);
//无参信号和槽连接
void(Teacher::*tearcherSignal)(void) = &Teacher::StandUp;
void(Student::*studentSlot)(void) = &Student::Hello;
connect(ls,tearcherSignal,xs,studentSlot);
//信号连接信号
connect(btn,&QPushButton::clicked,ls,tearcherSignal);
}
void Widget::ClassStart()
{
//上课,调用后, 老师触发信号
emit ls->StandUp("威武霸气!");
}
Widget::~Widget()
{
}
运行后点击按钮即可触发对应函数可直接触发老师类的StandUp,再触发学生类的Hello。
5.4.2 拓展
- 断开信号可以用disconnect函数,参数与connect函数中的一样。
- 一个信号可以连接多个槽函数
- 多个信号可以连接同一个槽函数
- 信号和槽函数的参数 必须类型一一对应
- 信号的参数个数 可以多余 槽的参数个数
- Qt4版本信号槽连接:connect(发送者,SIGNAL(信号函数),接收者,SLOT(槽函数));
- Qt4版本优点:参数直观;缺点:类型不做检测
6 Lambda表达式
6.1 介绍
C++11 中的Lambda表达式用于定义并创建匿名的函数对象,以简化编程工作。
-基本构成:
[capture](parameters)mutable->return-type
{
statement
}
[函数对象参数](操作符重载函数参数)mutable->返回值(函数体)
① 函数对象参数
[],标识一个lambda的开始,这部分不可省略。函数对象参数是传递给编译器自动生成的函数对象类的构造函数的。函数对象参数只能使用那些到定义lambda为止时lambda所在作用范围内可见的局部变量(包括lambda所在类的this)。函数对象参数有以下形式:
② 操作符重载函数参数
标识重载的()操作符的参数,没有参数时,这部分可以省略。参数可以通过按值(如:(a,b))和按引用(如:(&a,&b))两种方式进行传递。
③ 可修改标识符
mutable声明,这部分可以省略。按值传递函数对象参数时,加上mutable修饰符后,可以修改按值传递进来的拷贝(注意是能修改拷贝,而不是值本身)。
④ 函数返回值
->返回值类型,标识函数返回值的类型,当返回值为void,或者函数体中只有一处return的地方(此时编译器可以自动推断出返回值类型)时,这部分可以省略。
⑤ 函数体
{},标号函数的实现,这部分不能省略,但函数体可以为空。
6.2 试用样例
#include "widget.h"
#include <QPushButton>
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
QPushButton * btn = new QPushButton;
btn->setParent(this);
btn->show();
int m = 10;
connect(btn,&QPushButton::clicked,this,[m]()mutable{
m= 10+100;
qDebug()<<m;
});
connect(btn,&QPushButton::clicked,this,[m](){
qDebug()<<m;
});
int k = 1000;
int ret = [=]()->int{return k;}();//后面加括号调用函数
qDebug()<<ret;
}
Widget::~Widget()
{
}
先输出1000,然后每次点击按钮会输出110和10。
注意:在上述代码中按钮连接的lambda表达式中不能用k值,因为不在作用域内;如果lambda第三个参数是this可以省略该参数。
lambda表达 最常用:[=](){}
7 小作业
要求实现:一个按钮开始显示“Open",点击Open打开窗口并且显示改为"Close",点击Close又会关闭打开的窗口,并且文本变回"Open”。
代码如下:
#include "widget.h"
#include <QPushButton>
#include <QDebug>
#include <QWindow>
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
QPushButton * btn = new QPushButton("Open",this);
btn->show();
QWindow * myWindow = new QWindow;
bool flag = false;
connect(btn,&QPushButton::clicked,[=]()mutable{
if(!flag){
btn->setText("Close");
myWindow->show();
}else{
btn->setText("Open");
myWindow->close();
}
flag = !flag;
});
}
Widget::~Widget()
{
}