一、自定义信号和槽
1、基本语法
在Qt中,允许自定义信号的发送接收方以及接收方,即可以自定义信号函数和槽函数。但是对于自定义信号的信号函数和槽函数有一定的书写规则。

Qt中如果要让某个类能够使用信号槽(可以在类中定义信号和槽函数),则必须要在类最开始的地方写下Q_OBJECT宏(这个宏可以展开很多代码)。如果不加上这个宏,那么编译的时候就会报错。这个宏不需要我们自己手动添加,一般Qt create会帮助我们自动生成
(1)自定义信号函数书写规范
自定义信号在实际开发中很少需要用到,Qt内置的信号就足以应付大部分的开发场景
所谓Qt的信号,本质上也就是一个函数,在Qt5以及更高的版本中,槽函数和普通的成员函数之间没有什么差别。但是,信号是一类非常特殊的函数,我们只要写出函数声明,并告诉Qt这是一个信号即可。这个函数的定义是Qt在编译过程中自动生成的
1、自定义信号函数必须写到"signals"下
2、返回值为void,只需要声明,不需要实现
3、可以有参数,也可以发送重载

此处的signals是Qt自己扩展的关键字。qmake构建Qt项目时,就会调用一些代码的分析/生成工具,扫描到类中包含signals这个关键字时,就会自动的把下面的函数声明认为是信号,并且给这些信号函数自动的生成函数定义
(2)自定义槽函数书写规范
所谓的自定义一个槽函数,其操作规律和自定义一个普通成员函数没有什么区别
1、早期的Qt版本要求槽函数必须写到"public slots"下,但是现在高级版本的Qt允许写到类的"public"作用域中或者全局下
2、返回值为void,需要声明,也需要实现
3、可以有参数,可以发生重载
槽声明的两种写法:


此处的slots是Qt自己扩展的关键字,不是C++标准中的写法。qmake构建Qt项目时,就会调用专门的扫描器,扫描代码中特定的关键字(比如slots),基于关键字自动生成一大堆相关代码
(3)发送信号
使用**"emit"**关键字发送信号。"emit"是一个空的宏。"emit"其实是可选的,没有什么含义,只是为了提醒开发人员
(4)示例
A.一
a.在widget.h中声明自定义的信号和槽

b.在widget.cpp中实现槽函数,并且关联信号和槽
首先关联信号和槽,一旦检测到信号发送之后就会立刻执行关联的槽函数。反之,若先发射信号,此时还没有关联槽函数,当信号发射之后槽函数就不会响应

B.二------当老师说"上课了",学生们就"回到座位,开始学习"
在源文件中新建两个类,一个是老师类,一个是学生类:首先选中项目名称,鼠标右键------>"add new..."

点击 add new 之后,出现如下界面

选择"choose"出现如下界面

注意:在Qt中新建类时,要选择新建类的父类
显然,当前项目中还没有什么类适合做新类的父类,同时新的类也不是一个"窗口"或者"控件",这种请款一般选择QObject作为基类。这样做的好处是这个新类的对象可以搭配Qt的对象树机制,便于对象的正确释放
选择"下一步",出现如下见面

对于"学生类"以上述同样的方式进行添加,添加完成之后,项目目录新增文件如下:

在 student.h 中声明槽函数:

在 widget.h 中实例化 "老师类对象" 和 "学生类对象":

在 student.cpp 中实现槽函数:

在 widget.cpp 中连接自定义信号和槽:

运行结果如下图所示:

C. 三 ------ 老师点击 "按钮" 触发学生上课

运行结果如下图所示:

2、带参数的信号和槽
Qt的信号槽也支持带有参数,同时也可以支持重载。要求:信号函数的参数列表要和对应连接的槽函数参数列表一致。(一致主要是要求类型,个数如果不一致也可以,但要求信号的参数个数比槽的参数个数多)
此时信号触发,调用到槽函数的时候,信号函数中的实参就能够被传递到槽函数的形参中。通过这样的机制就可以让信号给槽传递数据了
(1)重载信号槽
A. 在 "widget.h" 头文件中声明重载的信号函数以及重载的槽函数

B. 在 "Widget.cpp" 文件实现重载槽函数以及连接信号和槽

C. 执行结果

(2)信号槽参数列表匹配规则
A. 在 "widget.h" 头文件中声明信号和槽函数

B. 在 "widget.cpp" 文件中实现槽函数以及连接信号和槽

其实信号的参数个数可以多于槽函数的参数个数,但是槽的参数个数不能多于信号参数个数。但是实际开发中最好还是保持参数个数也能匹配⼀致。
(3)信号的参数个数多于槽函数的参数个数
A. 在 "widget.h" 头文件中声明信号和槽函数

B. 在 "widget.cpp" 文件中实现槽函数以及连接信号和槽

二、信号与槽的连接方式
1、一对一
主要有两种形式,分别是
1、一个信号连接一个槽
2、一个信号连接一个信号
(1)一个信号连接一个槽

在 "widget.h" 中声明信号和槽以及信号发射函数:

在 "widget.cpp" 中实现槽函数,信号发射函数以及连接信号和槽

(2)一个信号连接另一个信号

在上一段代码的基础上,在 "widget.cpp" 文件中添加如下代码:

2、一对多
一个信号连接多个槽:

在 "widget.h" 头文件中声明一个信号和三个槽:

在 "widget.cpp" 文件中实现槽函数以及连接信号和槽:

3、多对一
多个信号连接一个槽函数

在 "widget.h" 头文件中声明两个信号以及一个槽:

在 "widget.cpp" 文件中实现槽函数以及连接信号和槽:

4、多对多
多个信号连接多个槽函数:
在 "widget.h" 头文件中声明三个信号以及三个槽:

在 "widget.cpp" 文件中实现槽函数以及连接信号和槽:

实际上,随着程序员经验越来越多,在GUI开发的过程中,"多对多"其实是个"伪需求",实际开发会很少用到,绝大部分情况来说,"一对一"就够用了
信号槽存在的意义:
- 解耦合(写代码追求"高内聚,低耦合")
- 多对多(非常类似于Mysql中的"多对多")