文章目录
1. 信号和槽的基本概念
信号源:由那个控件发出的信号
信号的类型:用户进行不同的操作 就可能触发不同的信号
(点击按钮 触发点击信号)
信号的处理方式:槽 (slot) (槽相当于一个函数)
Qt中使用 connect 这样的函数 把一个信号和 一个槽 关联起来
只要信号触发 Qt就会自动执行槽函数
( 槽函数 本质是一种 回调函数 )
处理的先后顺序
回到家 你妈面色不善 你脑袋里一顿回想 是因为最近考试考的很差 所以要跟你妈表决心 一定好好学
信号源:你妈
信号类型:面色不善
信号处理方式:脑袋里一顿回想 是因为最近考试考的很差 -> 要跟你妈表决心 一定好好学
一定要把信号的处理方式准备好 再触发信号
Qt中 一定是先关联信号 和 槽 再触发信号 顺序不能颠倒 否则 信号就不知道如何处理
(你妈面色不善 你没有想到你做过什么以及解决办法 那留给你的只有一顿毒打)
connect 函数的用法
connect 是 QObject 提供的 静态成员函数
![](https://i-blog.csdnimg.cn/direct/bb0c35030fd449de8c46cf22326f73bb.png)
connect 有 五个参数 但最后一个参数提供了默认参数 所以一般只考虑前四个参数
第一个参数 sender 表示信号源 描述了当前信号是那个控件发出来的
第二个参数 signal 表示信号的类型 发出哪一种信号
第三个参数 receiver 表示 那个对象负责处理
第四个参数 method 表示 这个对象该怎么处理
connect 函数 使用练习
![](https://i-blog.csdnimg.cn/direct/af84f97a28404b2c91ae5bfc60684e7a.png)
new 一个 对象 button ( QPushButton 表示普通按钮) 其父对象为 Widget对象 (详细介绍请翻前几篇Qt博客)
使用 setText 函数 设置文本 关闭
使用 move 函数 将文本 移动
![](https://i-blog.csdnimg.cn/direct/42780f2073cb4ae6be15453a1afc9a38.png)
此时运行程序后 显示的关闭 按钮 按并没有反应 还需 通过 connect函数 将 信号和 槽 连接
![](https://i-blog.csdnimg.cn/direct/a9c2ebfb6636447d8f7cd146bfec7ab4.png)
![](https://i-blog.csdnimg.cn/direct/3c43cb29c29a45058315ae51814ee042.png)
当输入 connect 函数的 第二个参数 时 会出现 click 和 clicked
click 为 slot (槽)函数 作用为 调用时 相当于 点击了一下按钮 (点击的动作)
clicked 为 信号函数 即 要触发的点击信号 (点击完成了)
当输入 connect 函数的 第四个参数 时
close 是 QWidget 内置的槽函数
Widget 继承自 QWidget 也就继承了 父亲的槽函数
具体作用为 关闭当前窗口 或控件
当前信号由 button 发出 使用 clicked 触发点击信号 this作为一个指针 指向了 Widget 对象 由Widget对象负责处理
使用 close 函数 关闭当前窗口
即 针对 button 按钮 进行点击 Widget 就会关闭
![](https://i-blog.csdnimg.cn/direct/4d2d6dd3153b4bd18d59c355712bb72b.png)
点击 关闭按钮 当前 Widget 窗口 就会 关闭
2. 自定义 信号 和 槽
自定义槽函数
第二种定义槽函数的方式
![](https://i-blog.csdnimg.cn/direct/44bc79657b8b45d8977aed2521633ed1.png)
connect 的第四个参数 作为 自定义的槽函数 handleClicked
即自己定义在Widget窗口下如何操作
![](https://i-blog.csdnimg.cn/direct/5438628c86ef4acaaf492767e1e216f8.png)
槽 就是一个 普通的成员函数 即自定义槽函数 和 自定义 普通成员函数 相同
所以 在 widget.h中 添加 共有的成员函数 handleClicked
![](https://i-blog.csdnimg.cn/direct/acc5a229b5bd4d61b6538e59141320b6.png)
在 widget.cpp 中 对 自定义槽函数 handleClicked 进行定义
在Widget窗口中 若按下按钮 显示 按钮已经按下
![](https://i-blog.csdnimg.cn/direct/cbd8e4b6c49e420da4b1982351bedb8f.png)
运行程序 按下按钮后 由 Widget 变为 按钮已经按下
第二种定义槽函数的方式
![](https://i-blog.csdnimg.cn/direct/51f36d979d83466fa064fb8e43fa97ea.png)
打开ui界面 拖动 Push Button (普通按钮) 到右侧框中
并修改 为 按钮
![](https://i-blog.csdnimg.cn/direct/51bdd6b314304a939cbfb92e4461a4f1.png)
此时 ui 界面 右侧的 objectName 为 pushButton
通过 ui->pushButton 的方式 就可以获取到 界面上 拖上去的这个按钮
![](https://i-blog.csdnimg.cn/direct/a3dc8e77f996434e951e7ed06a01932a.png)
在ui界面上 右击按钮 选择转到槽
![](https://i-blog.csdnimg.cn/direct/74dff2c8d4d2494d80c801132866be59.png)
在对话框中 说明 QPushButton 都提供那些信号 (包含其父类的信号)
![](https://i-blog.csdnimg.cn/direct/7ce806e1c77147c8a8cd5e15fa4c3789.png)
双击 clicked 就会直接生成好了 函数的声明 和 定义
![](https://i-blog.csdnimg.cn/direct/16507c06ab61493f91ade28d34dba0aa.png)
运行程序后 点击按钮 依旧从 Widget 变为 按钮已经按下
![](https://i-blog.csdnimg.cn/direct/3e917729f0d143eabefbc03d7bc8411f.png)
在Qt中 除了通过 使用 connect 来连接 信号槽 之外 还可以通过 函数名字的方式来自动连接
on 作为前缀名字 pushButton 是 按钮的 objectName clicked 作为 信号 的名字
当函数名符合上述规则后 Qt就会自动把信号 和 槽 建立联系
自定义信号
信号对应到 用户的某个操作
Qt 信号 是一个 特殊的 函数
1. 只需写出函数声明 告诉 Qt 这是一个信号 即可
这个函数的定义 是在 Qt 编译过程中 自动生成的 (自动生成的过程 无法干预)
2.作为信号函数 这个函数的返回值 必须为 void
有没有参数都可以 也可以支持重载
![](https://i-blog.csdnimg.cn/direct/62e1d377f97b4f5da31b35c67cf4048b.png)
signals 是 Qt自己扩展的关键字
扫描到类中包含 signals 这个关键字的时候 就会自动 把 下面的 函数声明 认为 是 信号 并且 给 这些信号函数 自动生成函数定义
![](https://i-blog.csdnimg.cn/direct/8bcb3ac5876c45cf86072188644a46df.png)
给 mySiganl 这样的函数声明 当成信号 并且给信号自动生成对应的函数定义
![](https://i-blog.csdnimg.cn/direct/fa8dc2aa22e949219664b341b4ce4dd4.png)
声明一个槽函数 handleMySignal
并在内部使用 setWindowsTitle 设置窗口标题
![](https://i-blog.csdnimg.cn/direct/f62ce55d36e74508bed0bbcfe790fb9a.png)
由 this (Widget对象) 发出 mySignal 信号 再由 this (Widget对象) 通过 handleMySignal 处理
![](https://i-blog.csdnimg.cn/direct/a4030951c4af4c9e9f1fe9ee1b0d05a6.png)
此时运行程序 发现信号并没有发出来
Qt内置的信号 不需要手动通过代码触发
用户在 GUI 进行某些操作 就会自动触发对应信号
![](https://i-blog.csdnimg.cn/direct/37b6ab7857a4465b939f23ab4506eda2.png)
Qt中 提供 关键字 emit (发送)
通过这样的方式 就可以把信号发射出去
此时的窗口标题 发生了改变
3. 带参数的 信号 和 槽
当信号带有参数时 槽的参数 必须和信号的参数一致
(理想情况下是这样 也可以不一样 后面会说特殊情况)
此时发射信号的时候 就可以给信号函数传递实参 与之对应的 这个参数 就会被传递到对应的槽函数中
就可以起到让信号给槽传参效果了
![](https://i-blog.csdnimg.cn/direct/0d3bc801ca60496f859da8e6bb499ac3.png)
当写函数声明时 只需写形参的类型即可 不用写形参的名字
![](https://i-blog.csdnimg.cn/direct/e4a46570977341f8b2074da383e749da.png)
发送信号时 信号带有的实参会把其传递到 槽函数的形参中
![](https://i-blog.csdnimg.cn/direct/d8d9059f11604f1c94c133bbcdf4e855.png)
运行程序后 点击按钮 发现窗口标题改变了
传参可以起到 复用代码 的效果
有多个逻辑 逻辑上基本一致 但涉及到的数据不同
就可以通过函数 来复用代码 并在不同的场景中传入不同的参数
![](https://i-blog.csdnimg.cn/direct/8256f4191175478b8efd40eb350dde62.png)
此时只有一个connect 函数 将 信号和槽连接
同样都是 mySignal信号 但是其中内容不同
通过一套信号槽 搭配不同的参数 就可以起到设置不同标题的效果
![](https://i-blog.csdnimg.cn/direct/9bd45a4da4e3420f9c1ea32f3a4ea8ee.png)
运行程序后 若点击设置文本1为标题 则 窗口 改变为 把标题设置为标题1
若点击设置文本2为标题 则 窗口 改变为 把标题设置为标题2
参数个数的影响
![](https://i-blog.csdnimg.cn/direct/3a1849f4f0f749e6ba2e0d9f98ceb2c8.png)
信号的参数有两个 而槽的参数有一个
![](https://i-blog.csdnimg.cn/direct/52d8d6db253d4669bc05664e9b3fb8a0.png)
运行程序后 发现功能依旧正常实现
信号函数的参数个数 超过了槽函数的参数个数 都是可以正常使用的
![](https://i-blog.csdnimg.cn/direct/98444bc7e8ab41098a802c7077483a20.png)
信号参数有一个 槽函数的参数有两个
![](https://i-blog.csdnimg.cn/direct/2b8607826f1f4c20b43342e6e423e95e.png)
运行程序后 发现会报错
信号函数的参数个数 少于槽函数的参数个数 此时代码无法编译通过
参数类型的影响
![](https://i-blog.csdnimg.cn/direct/76e39169f5b34915b61e8116c5cdc55d.png)
信号的参数类型 从 const QString 变为 int
![](https://i-blog.csdnimg.cn/direct/7072593cb37f4477ac385a96285cddb4.png)
当发出信号后 将 10 和 20 传递给 text
但是由于 两者 参数类型不一致 就导致报错
4. 信号和槽存在的意义
信号槽 解决的问题是 响应用户的操作
一个信号 可以 connect 到多个槽函数
一个槽函数 也可以被多个信号 connect
![](https://i-blog.csdnimg.cn/direct/ec0f1bdb2b114172a84858f338d649e2.png)
声明三个信号 和 三个 槽函数
![](https://i-blog.csdnimg.cn/direct/6cf3af7bd402438795bcf9b182bbd4c3.png)
使用connect函数
将 mysignal1 信号 与 mySlot1 槽函数 关联
将 mysignal1 信号 与 mySlot2 槽函数 关联
将 mysignal2 信号 与 mySlot1 槽函数 关联
将 mysignal1 信号 与 mySlot1 槽函数 关联
Qt引入 信号槽机制 本质是为了能够让 信号和槽 之间按照 多对多的方式 来进行 关联
(一个信号可以关联多个槽 一个槽可以关联多个信号)
5. 信号和槽 的断开
使用 disconnect 断开 信号槽的连接
![](https://i-blog.csdnimg.cn/direct/913f2da3a99a46b3a74183ef48888062.png)
声明两个槽函数 handleClick 和 handleClick2 并分别定义
![](https://i-blog.csdnimg.cn/direct/4e39d23a939c489db94683e38b0261f0.png)
先使用 connect 函数 将ui界面 pushButton 按钮 与 handleClick 槽函数 连接
![](https://i-blog.csdnimg.cn/direct/0368255980f3460fbdba9f47c5f75463.png)
再使用 disconnect 函数 将 pushButton 按钮 与 handleClick 槽函数 连接断开
重新使用 connect 函数 将 pushButton 按钮 与 handleClick_2 槽函数 连接
![](https://i-blog.csdnimg.cn/direct/cb28c3953dfd4c83837872ed0f32c3d1.png)
运行程序后 出现两个按钮
![](https://i-blog.csdnimg.cn/direct/3c4286b8cc16422b9b58addefdf09549.png)
先点击 修改窗口标题按钮 发送信号 使得 窗口变为 修改标题
![](https://i-blog.csdnimg.cn/direct/6a997b8fa38c4a5c9348f0df16d5958c.png)
点击 切换槽函数 按钮 发送信号 使得PushButton按钮与 handleClick 槽函数 断开 与 handleClick 函数连接
![](https://i-blog.csdnimg.cn/direct/198a49e5596347319af07036c6130212.png)
再次点击 修改窗口标题按钮 此时窗口为 修改标题2
6.使用 lambda表达式 定义 槽函数
定义槽函数时 可以使用 lambda表达式
圆括号为 形参列表 大括号为 函数体内容
![](https://i-blog.csdnimg.cn/direct/c2f504a333e94b6cbe095df52aded406.png)
之前还需要在 Widget类中 写一个 槽函数的声明 再回到 Widget.cpp文件中 写一个 槽函数的定义
现在只需要 写一个 lambda 表达式 即可
![](https://i-blog.csdnimg.cn/direct/6cf52577fd3d480ea657f3f89c8ff635.png)
由于lambda表达式 是一个 回调函数
这个函数 无法直接获取到上层作用域的变量
lambda 为了解决这个问题 引入了 变量捕获 的概念
通过变量捕获 获取到 外层 作用域中的变量
![](https://i-blog.csdnimg.cn/direct/e4a7c7ff37084a588ac870beaeaa6526.png)
将button变量名 捕获到 lambda表达式里面 就可以正常运行了
![](https://i-blog.csdnimg.cn/direct/65edfb88616149d285f2c3077475332f.png)
由于 button 是 new 出来的变量 所以 生命周期 随着整个窗口(挂到对象树上 窗口关闭才会释放)
![](https://i-blog.csdnimg.cn/direct/a6a5f8081b134b8d81e17fe619782f23.png)
this 指向的父对象为 Widget 对象
该变量放在main函数结束后才销毁 只要进程不结束 widget就可以用 对应的this 也就 可用
lambda表达式 可以简化 槽函数的定义