【从零开始学习QT】信号和槽
目录
一、信号和槽概述
信号的本质
槽的本质
二、信号和槽的使用
2.1 连接信号和槽
2.2 查看内置信号和槽
2.3 通过 Qt Creator 生成信号槽代码
自定义槽函数
自定义信号
自定义信号和槽
2.4 带参数的信号和槽
三、信号与槽的连接方式
3.1 一对一
(1)一个信号连接一个槽
(2)一个信号连接另一个信号
3.2 一对多
3.3 多对一
四、信号与槽的连接
五、使用 Lambda 表达式定义槽函数
QT专栏:QT_uyeonashi的博客-CSDN博客
一、信号和槽概述
在 Qt 中,用户和控件的每次交互过程称为一个事件。比如 "用户点击按钮" 是一个事件,"用户关闭窗口" 也是一个事件。每个事件都会发出一个信号,例如用户点击按钮会发出 "按钮被点击" 的信号,用户关闭窗口会发出 "窗口被关闭" 的信号。
Qt 中的所有控件都具有接收信号的能力,一个控件还可以接收多个不同的信号。对于接收到的每个信号,控件都会做出相应的响应动作。例如,按钮所在的窗口接收到 "按钮被点击" 的信号后,会做出 "关闭自己" 的响应动作;再比如输入框自己接收到 "输入框被点击" 的信号后,会做出 "显示闪烁的光标,等待用户输入数据" 的响应动作。在 Qt 中,对信号做出的响应动作就称之为槽。
信号和槽是 Qt 特有的消息传输机制,它能将相互独立的控件关联起来。比如,"按钮" 和 "窗口"
本身是两个独立的控件,点击 "按钮" 并不会对 "窗口" 造成任何影响。通过信号和槽机制,可以将 "按钮" 和 "窗口" 关联起来,实现 "点击按钮会使窗口关闭" 的效果。
信号的本质
信号是由于用户对窗口或控件进行了某些操作,导致窗口或控件产生了某个特定事件,这时 Qt 对
应的窗口类会发出某个信号,以此对用户的操作做出反应。因此,信号的本质就是事件。如:
• 按钮单击、双击
• 窗口刷新
• 鼠标移动、鼠标按下、鼠标释放
• 键盘输入
那么在 Qt 中信号是通过什么形式呈现给使用者的呢?
• 我们对哪个窗口进行操作, 哪个窗口就可以捕捉到这些被触发的事件。
• 对于使用者来说触发了一个事件我们就可以得到 Qt 框架给我们发出的某个特定信号。
• 信号的呈现形式就是函数, 也就是说某个事件产生了, Qt 框架就会调用某个对应的信号函数, 通知使用者。
槽的本质
槽(Slot)就是对信号响应的函数。槽就是一个函数,与一般的 C++ 函数是一样的,可以定义在类的任何位置( public、protected 或 private ),可以具有任何参数,可以被重载,也可以被直接调用(但是不能有默认参数)。槽函数与一般的函数不同的是:槽函数可以与一个信号关联,当信号被发射时,关联的槽函数被自动执行。
说明
(1)信号和槽机制底层是通过函数间的相互调用实现的。每个信号都可以用函数来表示,称为信号函数;每个槽也可以用函数表示,称为槽函数。例如: "按钮被按下" 这个信号可以用 clicked() 函数表示,"窗口关闭" 这个槽可以用 close() 函数表示,假如使用信号和槽机制-
实现:"点击按钮会关闭窗口" 的功能,其实就是 clicked() 函数调用 close() 函数的效果。
(2)信号函数和槽函数通常位于某个类中,和普通的成员函数相比,它们的特别之处在于:
• 信号函数用 signals 关键字修饰,槽函数用 public slots、protected slots 或者 private slots 修饰。signals 和 slots 是 Qt 在 C++ 的基础上扩展的关键字,专门用来指明信号函数和槽函数;
• 信号函数只需要声明,不需要定义(实现),而槽函数需要定义(实现)。
二、信号和槽的使用
2.1 连接信号和槽
在 Qt 中,QObject 类提供了一个静态成员函数 connect() ,该函数专门用来关联指定的信号函数和槽函数。
QObject 是 Qt 内置的父类. Qt 中提供的很多类都是直接或者间接继承自 QObject
connect() 函数原型:
connect (const QObject *sender,const char * signal ,const QObject * receiver ,const char * method ,Qt::ConnectionType type = Qt::AutoConnection )
代码示例: 在窗口中设置一个按钮,当点击 "按钮" 时关闭 "窗口"
2.2 查看内置信号和槽
系统自带的信号和槽通常是通过 "Qt 帮助文档" 来查询
如上述示例,要查询 "按钮" 的信号,在帮助文档中输入:QPushButton,
• 首先可以在 "Contents" 中寻找关键字 signals,
• 如果没有找到, 继续去父类中查找. 因此我们去他的父类 QAbstractButton 中继续查找关键字signals
这里的 clicked() 就是要找的信号。槽函数的寻找方式和信号一样,只不过它的关键字是 slot 。
2.3 通过 Qt Creator 生成信号槽代码
Qt Creator 可以快速帮助我们生成信号槽相关的代码.
自定义槽函数
所谓的slot就是一个普通的成员函数
自定义一个槽函数,操作过程和自定义一个普通的成员函数,没啥区别
第一种方式:一个简单的代码
第二种方式:
->
QT Creator 直接给我们生成好了一个函数;
在 "widget.h" 头文件中自动添加槽函数的声明;在 "widget.cpp" 中自动生成槽函数定义.
自定义信号
Qwidget :咱们的Widget虽然还没有定义任何信号,由于继承自QWidget和QObject这两类已经提供了一些信号量可以直接使用
所谓的QT信号,本质上也是一个“函数”,Qt 5及更高版本中,槽函数和普通成员函数之间,没啥区别
1. 信号是一类非常特殊的函数,程序员只需要写出函数声明,并告诉Qt这是一个信号即可。这个函数的定义是Qt在编译过程中自动生成的(自动生成的过程,程序员无法干预)
信号在Qt中是特殊的机制,Qt生成的信号函数实现要配合Qt框架做很多既定的操作
2. 作为信号函数,这个函数的返回值必须是void,有没有函数都可以,甚至也可以支持重载
signals:这个也是Qt自己扩展出来的关键字~
qmake的时候调用一些代码的分析/生成工具,扫描到类中包含signals这个关键字的时候,此时就会自动把下面的函数声明认为是信号,并且把这些信号函数自动的生成函数定义
举个栗子:
我们运行之后发现并窗口没有变化
这里qt提供了一个关键字emit,使用 "emit" 关键字发送信号
自定义信号和槽
2.4 带参数的信号和槽
Qt 的信号和槽也支持带有参数, 同时也可以支持重载.
此处我们要求, 信号函数的参数列表要和对应连接的槽函数参数列表一致.
此时信号触发, 调用到槽函数的时候, 信号函数中的实参就能够被传递到槽函数的形参当中.
传参可以起到复用代码的效果;有多个逻辑,逻辑上基本整体一致,但是涉及到的数据不同
就可以通过函数-参数来复用代码,并且在不同的场景中传入不同的参数即可~
举个栗子:
注意:带有参数的信号,要求信号的参数和槽的参数要一致;类型要一直,参数个数要满足信号的参数个数要多于槽的参数个数
三、信号与槽的连接方式
3.1 一对一
主要有两种形式,分别是:一个信号连接一个槽 和 一个信号连接一个信号。
(1)一个信号连接一个槽
举个栗子:
1、在 "widget.h" 中声明信号和槽以及信号发射函数;
2、在 "widget.cpp" 中实现槽函数,信号发射函数以及连接信号和槽;
(2)一个信号连接另一个信号
举个栗子:
在上述示例的基础上,在 "widget.cpp" 文件中添加如下代码:
3.2 一对多
一个信号连接多个槽
举个栗子:
(1)在 "widget.h" 头文件中声明一个信号和三个槽;
(2)在 "widget.cpp" 文件中实现槽函数以及连接信号和槽;
3.3 多对一
多个信号连接一个槽函数
举个栗子:
(1)在 "widget.h" 头文件中声明两个信号以及一个槽;
(2)在 "widget.cpp" 文件中实现槽函数以及连接信号和槽;
四、信号与槽的连接
如果没有disconnect,就会构成一个信号绑定了两个槽函数,触发信号的时候就会两个槽函数都执行
使用disconnect来断开信号的连接,disconnect的使用方式和connect是非常类似的
五、使用 Lambda 表达式定义槽函数
Lambda表达式 的语法格式如下:
[ capture ] ( params ) opt -> ret {
Function body;
};
说明:
举个栗子:
注意:
lambda为了解决上述问题,引入了“变量捕获”,通过变量捕获,获取到外层作用域中的变量,即将变量名称写到[ ]中
如过变量特别多课以写成[=],这个写法的含义就是把上层作用域中所有的变量名都给捕获进来!
注意lambda在捕获的时候一定要保证对象是可用的(注意生命周期)
总结:信号与槽的优缺点
优点: 松散耦合
信号发送者不需要知道发出的信号被哪个对象的槽函数接收,槽函数也不需要知道哪些信号关联了自己,Qt的信号槽机制保证了信号与槽函数的调用。支持信号槽机制的类或者父类必须继承于 QObject类。
缺点: 效率较低
与回调函数相比,信号和槽稍微慢一些,因为它们提供了更高的灵活性,尽管在实际应用程序中差别不大。通过信号调用的槽函数比直接调用的速度慢约10倍(这是定位信号的接收对象所需的开销;遍历所有关联;编组/解组传递的参数;多线程时,信号可能需要排队),这种调用速度对性能要求不是非常高的场景是可以忽略的,是可以满足绝大部分场景。
本篇完!