一、信号和槽概述
在Qt中,用户和控件的每次交互过程称为一个事件。比如"用户点击按钮"是一个事件,"用户关闭窗口"也是一个事件。每个事件都会发生出一个信号,例如用户点击按钮会发出"按钮被点击"的信号,用户关闭窗口会发出"窗口被关闭的信号",
Qt中的所有控件都具有接收信号的能力,一个空间还可以接受多个不同的信号。对于接收到的每个信号,控件都会做出相应的响应动作 。例如,按钮所在的窗口接受到'按钮被点击"的信号后,会做出"关闭自己"的动作;再比如输入框自己接收到"输入框被点击"的信号后,会做出"显示闪烁的光标,等待用户输入数据"的响应动作。再Qt中,对信号做出的响应动作就称之为槽。
信号和槽是Qt特有的消息传输机制,可以通过connect这样的函数,把一个信号和一个槽这种相互独立的控件关联起来。比如,"按钮"和"窗口"本身是两个独立的控件,点击"按钮"并不会对"窗口"造成任何影响。通过信号和槽机制,可以将"按钮"和"窗口"关联起来,实现"点击按钮会使窗口关闭"的效果

1、信号本质
信号是由于用户对窗口或控件进行了某些操作,导致窗口或控件产生了某个特定事件,这时Qtr对应的窗口类会发出某个信号,以此对用户的操作做出反应。因此,信号的本质就是事件。
- 按钮单击、双击
- 窗口刷新
- 鼠标移动、鼠标按下、鼠标释放
- 键盘输入
那么再Qt中信号是通过什么形式呈现给使用者的呢?
- 我们对哪个窗口进行操作,哪个窗口就可以捕捉到这些被触发的事件
- 对于使用者来说触发了一个事件,我们就可以得到Qt框架给我们发出的某个特定信号
- 信号的呈现形式就是函数,也就是说某个事件产生了,Qt框架就会调用某个对应的信号函数,通知使用者
在Qt中信号的发出者是某个实例化的类对象
2、槽的本质
槽(Slot)就是对信号响应的函数 。槽就是一个函数,与一般的C++函数是一样的,可以定义在类的任何位置(public、protected或private),可以具有任何参数,也可以被重载,也可以被直接调用(但是不能有默认参数)
槽函数与一般的函数不同的是:槽函数可以与一个信号关联,当信号被发射时,关联的槽函数被自动执行
所谓的槽函数本质上也是一种回调函数
【C语言阶段 - 函数指针】
- 实现转移表,降低代码的"圈复杂度"
- 实现回调函数效果(qsort)
【C++阶段】
- STL中,函数对象/坊函数
- lambda表达式
【Linux】
- 信号处理函数
- 线程的入口函数
- epoll基于回调的机制
(1)连接信号和槽机制底层是通过函数间的相互调用实现的
每个信号都可以用函数来表示,称为信号函数 ;每个槽也可以用函数表示,称为槽函数
例如:"按钮被按下"这个信号可以用cicked()函数表示,"窗口关闭"这个槽可以用close()函数表示,假如使用信号和槽机制 - 实现:"点击按钮会关闭窗口"的功能,其实就是clicked()函数调用close()的效果
(2)信号函数和槽函数通常位于某个类中
和普通的成员函数相比,它们的特别之处:
- **信号函数用signals关键字修饰,槽函数用public slots、protected slots或者private slots修饰。**signals和slots是Qt在C++的基础上扩展的关键字,专门用来指明信号函数和槽函数
- 信号函数只需要声明,不需要定义(实现),而槽函数需要定义(实现)
信号函数的定义是Qt自动在编译程序之前生成的,我们编写Qt应用程序无需关注这个,这种**自动生成代码的机制称为"元编程",**这种操作在很多场景中都能见到
二、信号和槽的使用
1、连接信号和槽
在Qt中,QObject类提供了一个静态成员函数connect(),该函数专门用来关联指定的信号函数和槽函数。
(1)关于QObject
QObject是Qt内置的父类,Qt中提供的很多类都是直接或者间接继承自QObject。
(2)connect()
A.函数原型
cpp
//描述当前信号是哪个控件发出的
connect (const QObject *sender,
//信号类型
const char * signal ,
//信号如何处理(哪个对象处理)
const QObject * receiver ,
//信号如何处理(这个对象该怎么处理------要处理信号的对象提供的成员函数)
const char * method ,
//暂时不考虑,很少使用
Qt::ConnectionType type = Qt::AutoConnection )
B.参数说明
- sender:信号的发送者
- signal:发送的信号(信号函数)
- receiver:信号的接收者
- method:接收信号的槽函数
- type:用于指定关联方式
c.代码示例
在窗口设置一个按钮,当点击"按钮"时关闭"窗口"

2、查看内置信号和槽
系统自带的信号和槽通常时通过"Qt帮助文档 "来查询的。如上述示例,要查询按钮的信号,在帮助文档输入:QpushButton 首先可以在"contents "中寻找关键字signals ,如果没有找到,继续去父类中查找,因此我们去他的父类QAbstractButton 中继续查找关键字signals:

这里的clicked()就是我们要找的信号。槽函数的寻找方式和信号一样,只不过他的关键字时slots
3、通过Qt Creator生成信号槽代码
Qt Create 可以快速帮助我们生成信号槽相关的代码
代码示例:在窗口中设置一个按钮,当点击按钮时关闭窗口
(1)新建项目,如下图为新建完成之后所包含的所有文件

(2)双击widget.ui文件,进入UI设计界面

(3)在UI设计窗口中拖入一个按钮,并且修改按钮的名称及字体大小

(4)可视化生成槽函数

当鼠标单机"转到槽..."之后,出现如下界面:对于按钮来说,当点击时发送信号是clicked(),所以此处选择:clicked()
这个窗口列出了QPushButton给我们提供的所有信号(还包含了QPushButton父类的信号)

对于普通按钮来说,使用clicked信号即可
clicked(bool)没有意义的,具有特殊状态的按钮(比如:复选按钮)才会用到clicked(bool)
(5)自动生成槽函数原型框架
A.在"widget.h"头文件中自动添加槽函数的声明

说明:自动生成槽函数的名称有一定的规则,槽函数的命名规则为:on_xxx_sss,其中:
以"**on"**开头,中间使用下划线连接起来
"xxx "表示的是对象名(控件的objectName属性)
"sss" 表示的是对应的信号
如:"on_pushButton_clicke d()",pushButton代表的是对象名,clicked是对应的信号。按照i这种命名风格定义的槽函数,就会被Qt自动的和对应的信号进行连接。但是我们日常写代码的时候,除非是IDE自动生成,否则最好还是不要去依赖命名规则,而是显示使用connect更好
- 一方面,显示connect可以更清晰直观的描述信号和槽的连接关系
- 另一方面,也是防止信号或者槽的名字拼写错误导致连接失败
当然,是配置大于约定,还是约定大于配置,那种更好。这样的话题业界桑存在争议,这里我个人还是建议优先考虑显示connect()
B.在widget.cpp中自动生成槽函数定义

(6)在槽函数函数定义中添加要实现的功能,实现关闭窗口的效果
