信号和槽
一、概述
在QT中,用户和控件的每次交互过程称为一个事件,每个事件都会发出信号,QT当中的每个控件都有接收信号的能力,对信号做出相应动作就称之为槽
信号的本质就是事件,用户对窗口或控件进行操作,比如单击、双击,比如键盘输入,会导致窗口或者控件产生某个特定事件,这时QT对应的窗口类会发出某个信号,以此对用户的操作做出反应
信号的呈现形式是函数,产生事件后QT框架会调用相对应的信号函数
在QT中信号的发出者是某个实例化的类对象
槽就是对信号响应的函数,槽函数除了可以与一个信号关联,当信号被发射时,关联的槽函数被自动执行以外,其他方面与一般的普通C++函数都是一样的
信号和槽机制底层是通过函数间的相互调用实现的,每个信号都可以用函数来表示,成为信号函数,每个槽也可以用函数表示,称为槽函数
信号函数和槽函数通常位于某个类中,和普通的成员函数相比它们有几个特别之处:
信号函数用signals关键词修饰,槽函数用public slots、protected slots、private slots修饰,signal和slots是QT在C++基础上扩展的关键字,专门用来指明信号函数和槽函数
信号函数只需要声明,不需要定义,而槽函数需要声明并定义
二、信号和槽的使用
连接信号和槽
QT中QIbject类提供了静态成员函数connect专门负责用来关联指定的信号函数和槽函数
connect函数原型:
connect(const QObject* sender,
const char* signal,
const QObject* receiver,
const char* method,
Qt::ConnectionType type = Qt::AutoConnection)
sender:信号的发送者
signal:发送的信号
receiver:信号接收者
method:接收信号的槽函数
type:用于指定关联方式,默认的关联方式为Qt::AutoConnection
现在我们来写一个按钮,这个按钮可以用来关闭窗口
纯代码实现
#include "widget.h"
#include <QPushButton>
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
QPushButton* btn = new QPushButton("关闭窗口",this);
connect(btn,&QPushButton::clicked,this,&QWidget::close);
}
Widget::~Widget()
{
}
可以实现点按按钮与关闭窗口的呼应
QT窗口信号和槽演示
可视化实现
在自动生成的函数中描述该按钮的槽
加一句关闭代码,由于此时的函数的隐藏参数为该按钮,所以直接操作就可以了
this->close();
QT关闭窗口信号与槽可视化构建
三、自定义信号和槽
1、基本语法
在QT中允许自定义信号的发送方和接收方,也就是说可以自定义信号函数和槽函数,但是有一些书写规范
(1)自定义信号函数书写规范
自定义信号函数必须写到signal下
返回值为void,只需声明,无需定义
可以有参数,可以发生重载
(2)自定义槽函数书写规范
可以写到public slots或者public以及全局下
返回值为void,需要声明以及定义
可以有参数,可以发生重载
(3)发生信号
使用emit关键字发送信号,emit是一个空的宏,没有含义,只是为了提醒开发人员,并且提高代码的可读性
将信号和槽连接,当发送信号时,槽做出反应,也就是执行槽函数
2、带参数的信号和槽
上面我们展示的是无参的信号和槽,那么有参数的信号和槽是怎么工作的呢
QT的信号和槽也支持带有参数,同时可以支持重载,但是我们要求信号函数的参数列表要和对应的槽函数参数列表一致
信号的参数个数可以多于槽函数的参数个数,但是槽的参数个数不能多于信号参数个数,当然最好还是一致
四、信号与槽的连接方式
1、一对一
(1)一个信号连接一个槽
在上文3.1.3中就是一个信号连接一个槽的情况,这是最常用的方法
(2)一个信号连接一个信号
将按钮的点击信号与我的自定义信号连接,将我的自定义信号和我的自定义槽连接,这时,点击按钮就会触发我的自定义槽的效果
信号连接信号,信号连接槽
2、一对多
(1)一个信号连接多个槽
一个信号和三个槽连接,触发信号三个槽都响应
(2)一个槽连接多个信号
三个信号和一个槽连接,每触发一个信号槽就响应一次
五、其他说明
1、信号与槽的断开
connect可以连接信号和槽,与之对应的,disconnect可以断开这个关系,用法与connect一致
2、connect函数的解析
在Qt5以前的版本中,connect的第二个和第四个参数是不允许任意函数的,使用的时候只能搭配着宏来使用,类似下方的代码
connect(this,SIGNAL(mysignal(),this,SLOT(myslot()));
这样会导致一个问题,它没有类型的检查,只要套上宏就可以用,如果出现以下情况,还是可以正常运行的,但信号与槽的参数列表不对应了,是错误的
connect(this,SIGNAL(mysignal(),this,SLOT(myslot(QStirng)));
当然现在我使用的QT5以及现在最新版本的QT6都没有这个问题了,该位置的参数可以是任意类型的
3、Lambda表达式
Lambda表达式是可以直接在connect中编写槽函数的一种方式,是C++11新增的特性,用来简化编程工作
语法格式:
[ capture ] ( params ) opt -> ret
{
Function body;
};
参数 | 作用 |
---|---|
capture | 捕获列表 |
params | 参数表 |
opt | 函数选项 |
ret | 返回值类型 |
Function body | 函数体 |
①[capture]标识一个Lambda表达式的开始,不可省略
符号 | 说明 |
---|---|
[ ] | 局部变量捕获列表,Lambda表达式不能访问外部函数体的任何局部变量 |
[a] | 在函数体内用值传递的方式访问a变量 |
[&a] | 在函数体内用引用传递的方式访问a变量 |
[=] | 以值传递的方式使用Lambda表达式外部的所有变量 |
[&] | 以引用的方式使用Lambda表达式外部的所有变量 |
[=,&foo] | foo使用引用方式传递,其余是值传递 |
[&,foo] | foo使用值传递方式传递,其余是引用方式传递 |
[this] | 在函数内部可以使用类的成员函数和成员变量 |
在Lambda表达式后加一个括号表示调用
②(params)函数参数表
值传递和引用传递,省略相当于无参
③opt选项
这部分可以省略,是一个选项,常用mutable声明
Lambda表达式外部的局部变量通过值传递进来时,默认为const,所以不能修改这个局部变量的拷贝,加上mutable就可以修改
mutable的使用
④->ret返回类型
可以指定Lambda表达式的返回类型,如果不指定则为编译器自己根据代码推导的一个返回类型,如果返回类型是void那不用写
⑤{Function body}函数体
函数定义在这大括号中
六、信号和槽的优缺点
优点:低耦合,信号和槽不是绑定在一起的,改变信号函数不会影响槽函数,改变槽函数也不会影响信号函数,它们只在发送信号的一瞬间建立联系
缺点:效率低,但这个低是相对于回调函数来说的,但是这个效率低的影响不大,因为对于人来说是感知不到的
今日分享就到这里~