1、信号与槽的概念
信号(signal)和槽(slot)是 Qt 的核心机制,也是在 PyQt 编程中对象之间进行通信的机制。在创建事件循环之后,通过建立信号和槽的连接就可以实现对象之间的通信。当信号发射(emit)时,连接的槽函数将会自动执行。
信号(signal)是在特定情况下被发射(emit)的一种通告。GUI 程序设计的主要内容就是对界面上各组件发射的特定信号进行响应,只需要知道什么情况下发射了哪些信号,然后合理地去响应和处理这些信号就可以了。
槽(slot)实质上是一个函数,可以被直接调用,是对信号响应的函数。槽函数与一般的函数不同的是:槽函数可以与一个信号关联,当信号被发射时,关联的槽函数会被自动执行。
在 Qt 编程中,通过 Qt 信号和槽机制对鼠标或键盘在界面上的操作进行响应处理。例如,对鼠标单击按钮的执行处理信号的操作。
PyQt 的窗口控件类中有很多内置信号,开发者也可以添加自定义信号。信号与槽具有如下特点:
2、信号与槽的基础函数
2.1、创建信号函数
本文仅描述主要的信号函数,具体详情可参照官方文档:Support for Signals and Slots — PyQt v5.15 Reference Guide
PyQt 的内置信号是自动定义的。使用 PyQt5.QtCore.pyqtSignal()函数可以为 QObject 创建一个信号,使用 pyqtSingnal()函数可以把信号定义为类的属性。pyqtSignal()函数信息如下图所以:
2.2、连接信号函数
使用 connect()函数可以把信号绑定到槽函数上。connect()函数信息如下图所示:
2.3、断开信号函数
使用 disconnect()函数可以解除信号与槽函数的绑定。disconnect()函数信息如下图所示:
2.4、发射信号函数
使用 emit()函数可以发射信号。emit()函数信息如下图所示:
3、信号和槽的使用方法
信号与槽有三种使用方法,第一种是内置信号与槽的使用,第二种是自定义信号与槽的使用,第三种是装饰器的信号与槽的使用。由于第三种方法本质上是第一种方法的衍生,因此这里简要介绍前两种方法的使用。
Qt Designer 中提供了一些最基础的信号和槽设置方法,在实际的项目开发中,信号和槽最佳的使用方式是 Qt Designer 和编程相结合,才能提高开发效率。
3.1、内置信号与槽的使用
所谓内置信号与槽的使用,是指在发射信号时,使用窗口控件的函数,而不是自定义的函数。在信号与槽中,可以通过 QObject.signal.connect 将一个 QObject 的信号连接到另一个 QObject 的槽函数。
可以在【编辑->Edit Signal/slot】中进行信号和槽设置。
进入信号槽编辑模式,可以直接在发射者(“Button"按钮)上按住鼠标左键不放,拖动到接收者(Form 窗体)上,这样就建立起了连接,如下图所示:
接着会弹出“配置连接"对话框,如下图所示:
可以看到按钮控件会发射很多内置信号和槽,选择所需信号,然后单击“OK"按钮,就会生成对应的槽函数处理。
例如,我想实现单击按钮关闭窗口的效果,所以这里勾选“显示从 QWidget 继承的信号和槽"复选框。
在左侧按钮的信号栏里选择 clicked()信号,在右侧的 Form 槽函数中选择 close(),这意味着对按钮单击会发射 clicked 信号,这个信号会被 Form 窗体的槽函数 close()捕捉到,并触发该窗体的 close 行为(也就是关闭该窗体)。
连接信号和槽成功后,会发现在 Edit Signal/slot(编辑信号/槽)模式下,所创建的信号和槽关系的连线是红色的,如下图所示:
接着将 UI 界面转换为 Python 文件,这里我使用 Eric 6 编译,不再赘述,效果如下所示:
拓展学习:Python Qt GUI设计:将UI文件转换为Python文件的三种妙招(基础篇—2)
UI 文件编译后代码如下所示:
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(383, 276)
self.pushButton = QtWidgets.QPushButton(Form)
self.pushButton.setGeometry(QtCore.QRect(140, 120, 93, 28))
self.pushButton.setObjectName("pushButton")
self.retranslateUi(Form)
self.pushButton.clicked.connect(Form.close)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "Form"))
self.pushButton.setText(_translate("Form", "Button"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
Form = QtWidgets.QWidget()
ui = Ui_Form()
ui.setupUi(Form)
Form.show()
sys.exit(app.exec_())
复制代码
代码中,通过 connect 函数连接按钮的 clicked()信号和槽函数 Form.close(),如下所示:
self.pushButton.clicked.connect(Form.close)
复制代码
运行程序,按钮是信号发射者,当单击按钮之后会发射一个信号,通过这行代码程序内部的通信机制知道这个按钮的单击事件被连接到窗体的关闭事件上,然后通知接收者窗体,可以运行槽函数 close(),实现窗口关闭。
3.2、自定义信号与槽的使用
自定义信号与槽是指在发射信号时,不使用窗口控件的函数,而是使用自定义的函数(简单地说,就是使用 pyqtSignal 类实例发射信号)。
之所以要使用自定义信号与槽,是因为通过内置函数发射信号有自身的缺陷,主要是以下三点:
内置函数只包含一些常用的信号,有些信号的发射找不到对应的内置函数;
内置函数只有在特定情况下(如按钮的点击事件)才能发射这种信号;
内置函数传递的参数是特定的,不可以自定义。使用自定义的信号函数则没有这些缺陷。
在 PyQt5 编程中,自定义信号与槽的适用范围很灵活。例如,因为业务需求,在程序中的某个地方需要发射一个信号,传递多种数据类型(实际上就是传递参数),然后在槽函数中接收传递过来的数据,这样就可以非常灵活地实现一些业务逻辑。
自定义信号的一般流程如下:
3.2.1、定义信号
使用 pyqtSingnal()函数可以把信号定义为类的属性,示例代码如下所示:
#无参数的信号
signal1=pyqtSignal()
#带一个参数(整数)的信号
signal2=pyqtSignal(int)
#带两个参数(整数,字符串)的信号
signal3=pyqtSignal(int,str)
#带一个参数(列表)的信号
signal4=pyqtSignal(list)
#带一个参数(字典)的信号
signal5=pyqtSignal(dict)
#带(整数 字符串)或者(字符串)的信号
signal6=pyqtSignal([int,str],[str])
复制代码
3.2.2、定义槽函数
定义一个槽函数,它有多个不同的输入参输数,示例代码如下所示:
def signalCall1( self ):
print("signal1 emit")
def signalCall2( self,val ):
print('signal2 emit,value:',val)
def signalCall3( self,val,text ):
print('signall3 emit,value:',val,text)
def signalCall4( self,val ):
print('signal4 emit,value:',val)
def signalCall5( self,val ):
print('signal5 emit,value',val)
def signalCall6( self,val,text ):
print('signal6 emit,value',val,text)
def signalCall7( self,val ):
print('signal6 ovetload emit',val)
复制代码
3.2.3、连接信号与槽函数
使用 connect()函数可以把信号绑定到槽函数上,示例代码如下所示:
#信号与槽函数的链接
self.signal1.connect(self.signalCall1)
self.signal2.connect(self.signalCall2)
self.signal3.connect(self.signalCall3)
self.signal4.connect(self.signalCall4)
self.signal5.connect(self.signalCall5)
self.signal6[int,str].connect(self.signalCall6)
self.signal6[str].connect(self.signalCall7)
复制代码
3.2.4、发射信号
使用 emit()函数可以发射信号,示例代码如下所示:
#信号发射
self.signal1.emit()
self.signal2.emit(1)
self.signal3.emit(1,'第三个')
self.signal4.emit([1,2,3,4])
self.signal5.emit({"name":'JIA','age':'21'})
self.signal6[int,str].emit(1,"第六")
self.signal6[str].emit('第六')
复制代码
3.2.5、实例
将上述片段代码,整合,非常简单,各位可以看着理解。
from PyQt5.QtCore import QObject,pyqtSignal
class CusSignal(QObject):
#无参数的信号
signal1=pyqtSignal()
#带一个参数(整数)的信号
signal2=pyqtSignal(int)
#带两个参数(整数,字符串)的信号
signal3=pyqtSignal(int,str)
#带一个参数(列表)的信号
signal4=pyqtSignal(list)
#带一个参数(字典)的信号
signal5=pyqtSignal(dict)
#带(整数 字符串)或者(字符串)的信号
signal6=pyqtSignal([int,str],[str])
def __init__(self,parent=None):
super(CusSignal, self).__init__(parent)
#信号与槽函数的链接
self.signal1.connect(self.signalCall1)
self.signal2.connect(self.signalCall2)
self.signal3.connect(self.signalCall3)
self.signal4.connect(self.signalCall4)
self.signal5.connect(self.signalCall5)
self.signal6[int,str].connect(self.signalCall6)
self.signal6[str].connect(self.signalCall7)
#信号发射
self.signal1.emit()
self.signal2.emit(1)
self.signal3.emit(1,'第三个')
self.signal4.emit([1,2,3,4])
self.signal5.emit({"name":'JIA','age':'21'})
self.signal6[int,str].emit(1,"第六")
self.signal6[str].emit('第六')
#槽函数
def signalCall1( self ):
print("signal1 emit")
def signalCall2( self,val ):
print('signal2 emit,value:',val)
def signalCall3( self,val,text ):
print('signall3 emit,value:',val,text)
def signalCall4( self,val ):
print('signal4 emit,value:',val)
def signalCall5( self,val ):
print('signal5 emit,value',val)
def signalCall6( self,val,text ):
print('signal6 emit,value',val,text)
def signalCall7( self,val ):
print('signal6 ovetload emit',val)
if __name__ == '__main__':
custSignal=CusSignal()
复制代码
运行效果如下所示:
关于信号与槽的使用方法就讲到这了,在后面的实践中我们再细聊,感兴趣小伙伴可看看以下的拓展学习链接~
参考资料、拓展学习:
《Python QT GUI 与数据可视化编程》作者:杨海玲责任编辑 (中国)王维波 栗宝
《PyQt5 快速开发与实战》王硕
官方网站:PyQt API中信号与槽详细解释
Qt for Python 信号和槽的使用详解
PyQt中的信号(signal)和槽(slot)机制以及Designer中的使用
Python GUI库图形界面开发之PyQt5信号与槽的高级使用技巧(自定义信号与槽)详解与实例
评论