PyQt: 信号-槽机制
信号-槽机制是QT的一个重要特征。该特征的存在可以方便不同对象之间的通信。当一些特定的事件发生时,可以发射一个信号。当该信号与某个或者某些槽连接在一起时,对应的槽会做出响应(执行相关代码)。
如果该信号不与任何槽存在连接,那么什么都不做。这也就是说信号发射之后,并不关心是否有对象去接受这个信号。
信号-槽机制具有以下特点:
1)一个信号可以与多个槽连接;
2)一个信号可以与其他信号连接;
3)信号的参数可以是任意类型;
4)一个槽可以连接多个信号;
5)信号与槽的连接可以断开;
6) 连接可以是同步的也可以是异步的;
7)连接是可以跨线程的。
定义新的信号函数
pyqt5自动地定义内置的信号函数,而新的信号函数需要通过pyqtSignal()来定义成所属类的属性。
一个信号具有三个方法: connect()、disconnect()、emit()。
信号可以被重载,但原有的信号函数会被当做是默认函数。自定义的新信号函数存在多个重载函数时,第一个被认为是默认的信号函数。
PyQt5.QtCore.pyqtSignal(types[, name[, revision=0[, arguments=[]]]])
from PyQt5.QtCore import QObject, pyqtSignal
class Foo(QObject):
# This defines a signal called 'closed' that takes no arguments.
closed = pyqtSignal()
# This defines a signal called 'rangeChanged' that takes two
# integer arguments.
range_changed = pyqtSignal(int, int, name='rangeChanged')
# This defines a signal called 'valueChanged' that has two overloads,
# one that takes an integer argument and one that takes a QString
# argument. Note that because we use a string to specify the type of
# the QString argument then this code will run under Python v2 and v3.
valueChanged = pyqtSignal([int], ['QString'])
当一个信号被发射时其传递的任何参数都会被转化成对应的C++类型。当信号的类型没有对应的C++类型时,其会被封装成一种可以使用的类型。因此,在定义新信号的时候需要注意参数类型。
class Foo(QObject):
# This will cause problems because each has the same C++ signature.
valueChanged = pyqtSignal([dict], [list])
定义新的槽函数
尽管pyqt5允许任意的可调用函数作为槽函数以接受信号。但有时候函数需要明确的指出某些方法是槽函数并给出相应的C++签名(signature)。在pyqt5中,函数pyqtSlot()承担了这一任务。
PyQt5.QtCore.pyqtSlot(types[, name[, result[, revision=0]]])
from PyQt5.QtCore import QObject, pyqtSlot
class Foo(QObject):
@pyqtSlot()
def foo(self):
""" C++: void foo() """
@pyqtSlot(int, str)
def foo(self, arg1, arg2):
""" C++: void foo(int, QString) """
@pyqtSlot(int, name='bar')
def foo(self, arg1):
""" C++: void bar(int) """
@pyqtSlot(int, result=int)
def foo(self, arg1):
""" C++: int foo(int) """
@pyqtSlot(int, QObject)
def foo(self, arg1):
""" C++: int foo(int, QObject *) """
也可以对同一个函数使用多个decorator。
from PyQt5.QtCore import QObject, pyqtSlot
class Foo(QObject):
@pyqtSlot(int)
@pyqtSlot('QString')
def valueChanged(self, value):
""" Two slots will be defined in the QMetaObject. """
连接、断开与发射信号
信号可以通过connect()函数与槽相连接。当连接失败时会抛出异常。其中的槽可以使可调用的函数或者其他信号。
connect(slot[, type=PyQt5.QtCore.Qt.AutoConnection[, no_receiver_check=False]])
信号与槽的断开连接通过函数disconnect()实现。当没有指定槽名称时,则默认断开所有连接。
disconnect([slot])
信号函数利用emit()函数来发射信号。
emit(*args)
from PyQt5.QtCore import QObject, pyqtSignal
class Foo(QObject):
# Define a new signal called 'trigger' that has no arguments.
trigger = pyqtSignal()
def connect_and_emit_trigger(self):
# Connect the trigger signal to a slot.
self.trigger.connect(self.handle_trigger)
# Emit the signal.
self.trigger.emit()
def handle_trigger(self):
# Show that the slot has been called.
print "trigger signal received"
也可以使用关键字参数来建立连接。
act = QAction("Action", self)
act.triggered.connect(self.on_triggered)
act = QAction("Action", self, triggered=self.on_triggered)
act = QAction("Action", self)
act.pyqtConfigure(triggered=self.on_triggered)
传递额外的参数
在编程的时候很多时候并不想重载已有的信号函数,而又想传递额外的参数。额外的参数有时候很有用,特别有时候可以提高模块的重复使用性以减少代码冗余。下面提供了两种可以传递额外参数的方法。
#-*-coding:utf-8-*-
from PyQt5.Qt import *
from functools import partial
class customWindow(QWidget):
def __init__(self):
super(customWindow, self).__init__()
self.setWindowTitle('Keep Learning Pyqt')
self.resize(600,100)
btn_1 = QPushButton("btn_1")
btn_2 = QPushButton("btn_2")
btn_3 = QPushButton("btn_3")
self.txt = QTextEdit()
btn_1.clicked.connect(self.btn_1_clicked)
btn_2.clicked.connect(lambda checked: self.btn_2_clicked(checked, 2)) # it also can receive the signal arguments
btn_3.clicked.connect(partial(self.btn_3_clicked, 3))
mainlayout = QHBoxLayout()
mainlayout.addWidget(btn_1)
mainlayout.addWidget(btn_2)
mainlayout.addWidget(btn_3)
mainlayout.addWidget(self.txt)
self.setLayout(mainlayout)
#===============define two function to show result========================
def btn_1_clicked(self):
str = "Button 1 is clicked by classical method"
self.txt.setText(str)
#===============use lambda expression to transmit addtional params=======
def btn_2_clicked(self,checked, n):
print checked
str = "Button {0} is clicked by lambda expression".format(n)
self.txt.setText(str)
#===============use partial function to transmit addtional params========
def btn_3_clicked(self, n):
str = "Button {0} is clicked by partial function".format(n)
self.txt.setText(str)
if __name__ =="__main__":
import sys
app = QApplication(sys.argv)
myWindow = customWindow()
myWindow.show()
app.exec_()