[QT] 信号和插槽
个人主页~
信号和槽
- 一、概述
- 二、信号和槽的使用
- 连接信号和槽
- 纯代码实现
- 可视化实现
- 三、自定义信号和槽
- 1、基本语法
- (1)自定义信号函数书写规范
- (2)自定义槽函数书写规范
- (3)发生信号
- 2、带参数的信号和槽
- 四、信号与槽的连接方式
- 1、一对一
- (1)一个信号连接一个槽
- (2)一个信号连接一个信号
- 2、一对多
- (1)一个信号连接多个槽
- (2)一个槽连接多个信号
- 五、其他说明
- 1、信号与槽的断开
- 2、connect函数的解析
- 3、Lambda表达式
- ①[capture]标识一个Lambda表达式的开始,不可省略
- ②(params)函数参数表
- ③opt选项
- ④->ret返回类型
- ⑤{Function body}函数体
- 六、信号和槽的优缺点
一、概述
在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}函数体
函数定义在这大括号中
六、信号和槽的优缺点
优点:低耦合,信号和槽不是绑定在一起的,改变信号函数不会影响槽函数,改变槽函数也不会影响信号函数,它们只在发送信号的一瞬间建立联系
缺点:效率低,但这个低是相对于回调函数来说的,但是这个效率低的影响不大,因为对于人来说是感知不到的
今日分享就到这里~
推荐阅读
-
交通目标识别数据集 YOLO 模型 ui 界面 ✓ 图像数量 15,000 张,包括 xml 和 txt 标记 11 个类别 交通道路车辆行人 红黄绿数据集 交通灯数据集 交通信号数据集
-
[QT] 信号和插槽
-
Qt 6 相对于 Qt 5 的主要改进和更新
-
Qt_Threads 简介和用法
-
Qt6 与 Qt5 相比的新功能和亮点
-
QT 开发 - Pyside]使用 Pycharm 和 conda 配置 Pyside 环境和新项目
-
ISP 基本框架和算法简介 ISP(图像信号处理器)
-
14-傅里叶变换的代码实现-一、numpy实现傅里叶变换和逆傅里叶变换 1.numpy实现傅里叶变换numpy.fft.fft2实现傅里叶变换,返回一个复数数组(complex ndarray),也就是频谱图像numpy.fft.fftshift将零频率分量移到频谱中心(将左上角的低频区域,移到中心位置) 20*np.log(np.abs(fshift))设置频谱的范围。可以理解为,之前通过傅里叶变换得到复数的数组,是不能通过图像的方法展示出来的,需要转换为灰度图像(映射到[0,255]区间)需要注意的是1> 傅里叶得到低频、高频信息,针对低频、高频处理能够实现不同的目的2> 傅里叶过程是可逆的,图像经过傅里叶变换、逆傅里叶变换后,能够恢复到原始图像3> 在频域对图像进行处理,在频域的处理会反映在逆变换图像上 # 将绘制的图显示在窗口 %matplotlib qt5 import cv2 import numpy as np import matplotlib.pyplot as plt img = cv2.imread(r"image\lena.bmp",cv2.IMREAD_GRAYSCALE) # 傅里叶变换 f = np.fft.fft2(img) # 移动中心位置 fshift = np.fft.fftshift(f) # 调整值范围 result = 20*np.log(np.abs(fshift)) plt.subplot(1,2,1) plt.imshow(img,cmap=plt.cm.gray) plt.title("original") plt.axis("off") plt.subplot(1,2,2) plt.imshow(result,cmap=plt.cm.gray) plt.title("result") plt.axis("off") plt.show 傅里叶变换的频谱图像: 2.numpy实现逆傅里叶变换numpy.fft.ifft2实现逆傅里叶变换,返回一个复数数组(complex ndarray)numpy.fft.ifftshiftfftshift函数的逆函数,将中心位置的低频,重新移到左上角iimg = np.abs(逆傅里叶变化结果)设置值的范围,映射到[0,255]区间 # 将绘制的图显示在窗口 %matplotlib qt5 import cv2 import numpy as np import matplotlib.pyplot as plt img = cv2.imread(r"image\boat.bmp",cv2.IMREAD_GRAYSCALE) # 傅里叶变换 f = np.fft.fft2(img) fshift = np.fft.fftshift(f) # 逆傅里叶变换 ishift = np.fft.ifftshift(fshift) iimg = np.fft.ifft2(ishift) iimg = np.abs(iimg) plt.subplot(1,2,1) plt.imshow(img,cmap=plt.cm.gray) plt.title("original") plt.axis("off") plt.subplot(1,2,2) plt.imshow(iimg,cmap=plt.cm.gray) plt.title("iimg") plt.axis("off") plt.show 将一副图像,进行傅里叶变换和逆傅里叶变换后,进行对比(一样的) 实例:通过numpy实现高通滤波,保留图像的边缘信息 获取图像的形状rows,cols = img.shape获取图像的中心点crow,ccol = int(rows/2),int(cols/2)将频谱图像的中心区域(低频区域)设置为0(黑色)fshift[crow-30:crow+30,ccol-30:ccol+30] = 0 # 将绘制的图显示在窗口 %matplotlib qt5 import cv2 import numpy as np import matplotlib.pyplot as plt img = cv2.imread(r"image\boat.bmp",cv2.IMREAD_GRAYSCALE) # 傅里叶变换 f = np.fft.fft2(img) fshift = np.fft.fftshift(f) # 高通滤波 rows,cols = img.shape crow,ccol = int(rows/2),int(cols/2) fshift[crow-30:crow+30,ccol-30:ccol+30] = 0 # 逆傅里叶变换 ishift = np.fft.ifftshift(fshift) iimg = np.fft.ifft2(ishift) iimg = np.abs(iimg) plt.subplot(1,2,1) plt.imshow(img,cmap=plt.cm.gray) plt.title("original") plt.axis("off") plt.subplot(1,2,2) plt.imshow(iimg,cmap=plt.cm.gray) plt.title("iimg") plt.axis("off") plt.show 使用numpy实现高通滤波的实验结果: 二、opencv实现傅里叶变换和逆傅里叶变换 1.opencv实现傅里叶变换 返回结果 = cv2.dft(原始图像,转换标识)1> 返回结果:是双通道的,第一个通道是结果的实数部分,第二个通道是结果的虚数部分2> 原始图像:输入图像要首先转换成np.float32(img)格式3> 转换标识:flags = cv2.DFT_COMPLEX_OUTPUT,输出一个复数阵列numpy.fft.fftshift将零频率分量移到频谱中心(将左上角的低频区域,移到中心位置)调整频谱的范围,将上面频谱图像的复数数组,转换为可以显示的灰度图像(映射到[0,255]区间)返回值 = 20*np.log(cv2.magnitude(参数1,参数2))1> 参数1:浮点型X坐标值,也就是实部2> 参数2:浮点型Y坐标值,也就是虚部 # 将绘制的图显示在窗口 %matplotlib qt5 import cv2 import numpy as np import matplotlib.pyplot as plt img = cv2.imread(r"image\lena.bmp",cv2.IMREAD_GRAYSCALE) # 傅里叶变换 dft = cv2.dft(np.float32(img),flags = cv2.DFT_COMPLEX_OUTPUT) # 移动中心位置 dftShift = np.fft.fftshift(dft) # 调整频谱的范围 result = 20*np.log(cv2.magnitude(dftShift[:,:,0],dftShift[:,:,1])) plt.subplot(1,2,1) plt.imshow(img,cmap=plt.cm.gray) plt.title("original") plt.axis("off") plt.subplot(1,2,2) plt.imshow(result,cmap=plt.cm.gray) plt.title("result") plt.axis("off") plt.show 傅里叶变换的频谱图像: 2.opencv实现逆傅里叶变换返回结果 = cv2.idft(原始数据)1> 返回结果:取决于原始数据的类型和大小2> 原始数据:实数或者复数均可numpy.fft.ifftshiftfftshift函数的逆函数,将中心位置的低频,重新移到左上角调整频谱的范围,映射到[0,255]区间返回值 = cv2.magnitude(参数1,参数2)1> 参数1:浮点型X坐标值,也就是实部2> 参数2:浮点型Y坐标值,也就是虚部 # 将绘制的图显示在窗口 %matplotlib qt5 import cv2 import numpy as np import matplotlib.pyplot as plt img = cv2.imread(r"image\lena.bmp",cv2.IMREAD_GRAYSCALE) # 傅里叶变换 dft = cv2.dft(np.float32(img),flags = cv2.DFT_COMPLEX_OUTPUT) dftShift = np.fft.fftshift(dft) # 逆傅里叶变换 ishift = np.fft.ifftshift(dftShift) iimg = cv2.idft(ishift) iimg = cv2.magnitude(iimg[:,:,0],iimg[:,:,1]) plt.subplot(1,2,1) plt.imshow(img,cmap=plt.cm.gray) plt.title("original") plt.axis("off") plt.subplot(1,2,2) plt.imshow(iimg,cmap=plt.cm.gray) plt.title("inverse") plt.axis("off") plt.show 将一副图像,进行傅里叶变换和逆傅里叶变换后,进行对比(一样的) 实例:通过opencv实现低通滤波,模糊一副图像
-
利用MATLAB实现音频信号的调制、解调、加噪、去噪和滤波操作
-
雷达信号处理:利用Matlab实现一维和二维自适应波束形成(DBF)