PyQt5:PyQt5 信号与槽(PyQt5的事件处理机制)
事件
PyQt5有一个独一无二的信号和槽机制来处理事件。信号和槽用于对象之间的通信。当指定事件发生,一个事件信号会被发射。槽可以被任何Python脚本调用。当和槽连接的信号被发射时,槽会被调用。调用示意图如图1所示:
信号和槽(槽函数)
在Qt中,所有继承Qwidget中的子对象(也就是控件)和QObject都支持信号与槽机制。
当信号发射时,连接的槽函数将自动执行。
早PyQt5中信号与槽通过Object.signal.connect()方法连接。
PyQt窗口有很多内置信号,开发者也可以自定义信号。
信号与槽具有如下特点:
- 一个信号可以连接多个槽
- 一个信号可以连接另一个信号
- 一个槽可以监听多个信号
- 信号与槽的连接方式可以是同步连接,也可以是异步连接
- 信号参数类型可以是python任何类型(
)
- 信号与槽的连接可能会跨线程
- 信号可能会断开
在Gui编程中,在编写一个类时,首先定义该类的信号与槽,在类中信号与槽进行连接,实现对象之间的数据传输;
当事件或者状态发生改变时,就会发出信号。同时,信号会触发所有与这个事件(信号)相关的函数(槽);
信号与槽可以是多对多的关系,一个信号可以连接多个槽,一个槽也可以监听多个信号。
高级自定义信号与槽
按照自己想要的方式自定义信号与槽函数,并传递参数,自定义信号流程一般如下:
- 定义信号
- 定义槽函数
- 连接信号与槽函数
- 发射信号
定义信号
通过类成员变量定义信号对象。使用PyQtSinnal()方法
定义槽函数
定义一个槽函数,有多个不同的输入参数。槽函数就是普通类中的函数或方法
连接信号与槽函数
通过connect方法连接信号与槽函数或者可调用对象
发射信号
通过emit()方法发射信号
实例:
from PyQt5.QtCore import QObject , pyqtSignal
class CustSignal(QObject):
#声明无参数的信号
signal1 = pyqtSignal()
#声明带一个int类型参数的信号
signal2 = pyqtSignal(int)
#声明带int和str类型参数的信号
signal3 = pyqtSignal(int,str)
#声明带一个列表类型参数的信号
signal4 = pyqtSignal(list)
#声明带一个字典类型参数的信号
signal5 = pyqtSignal(dict)
#声明一个多重载版本的信号,包括带int和str类型参数的信号和带str类型参数的信号
signal6 = pyqtSignal([int,str], [str])
def __init__(self,parent=None):
super(CustSignal,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.signalCall6OverLoad)
#发射信号
self.signal1.emit()
self.signal2.emit(1)
self.signal3.emit(1,"text")
self.signal4.emit([1,2,3,4])
self.signal5.emit({"name":"wangwu","age":"25"})
self.signal6[int,str].emit(1,"text")
self.signal6[str].emit("text")
def signalCall1(self):
print("signal1 emit")
def signalCall2(self,val):
print("signal2 emit,value:",val)
def signalCall3(self,val,text):
print("signal3 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 signalCall6OverLoad(self,val):
print("signal6 overload emit,value:",val)
if __name__ == '__main__':
custSignal = CustSignal()
运行结果如下:
signal1 emit
signal2 emit,value: 1
signal3 emit,value: 1 text
signal4 emit,value: [1, 2, 3, 4]
signal5 emit,value: {'name': 'wangwu', 'age': '25'}
signal6 emit,value: 1 text
signal6 overload emit,value: text
使用自定义参数
在PyQt编程过程中,经常会遇到给槽函数传递自定义参数的情况,比如有一个信号与槽函数的连接是:
button1.clicked.connect(show_page)
对于clicked信号来说,它是没有参数的;对于show_page函数来说,希望可以接收参数。
希望的show_page函数如下这样:
def show_page(self, name):
print(name," 点击啦")
问题是
信号发出的参数个数为0,槽函数接收的参数个数为1,由于0<1,这样运行起来一定会报错(原因是信号发出的参数个数一定要大于槽函数接收的参数个数)。解决这个问题就是:自定义参数的传递。
有两种解决方法,其中一种解决方法是使用lambda表达式。其完整代码如下:
from PyQt5.QtWidgets import QMainWindow, QPushButton , QWidget , QMessageBox, QApplication, QHBoxLayout
import sys
class WinForm(QMainWindow):
def __init__(self, parent=None):
super(WinForm, self).__init__(parent)
button1 = QPushButton('Button 1')
button2 = QPushButton('Button 2')
button1.clicked.connect(lambda: self.onButtonClick(1))
button2.clicked.connect(lambda: self.onButtonClick(2))
layout = QHBoxLayout()
layout.addWidget(button1)
layout.addWidget(button2)
main_frame = QWidget()
main_frame.setLayout(layout)
self.setCentralWidget(main_frame)
def onButtonClick(self, n):
print('Button {0} 被按下了'.format(n))
QMessageBox.information(self, "信息提示框", 'Button {0} clicked'.format(n))
if __name__ == "__main__":
app = QApplication(sys.argv)
form = WinForm()
form.setGeometry(300,300,600,400)
form.show()
sys.exit(app.exec_())
运行效果如下:
解释OnButtonClick()函数怎样处理两个按钮传递来的信号的:
使用lambda表达式传递按钮数字给槽函数,当然也可以传递其他任何东西,甚至是按钮控件本身(假设槽函数打算把传递信号的按钮修改为不可用的话)。
另一种解决方法就是使用functools中的partial函数。
不过,更推荐lambda函数。