本文主要介绍如何在PySide中使用Qt的信号和插槽,重点介绍新式的信号和插槽的书写方式(这种方式更加Pythonic),除此之外也会简单介绍一下旧式的信号插槽方式(仍然可用,但不建议使用)。
新式的信号插槽风格在PyQt的4.5版本中引入,PySide借鉴了这种处理方式,关于这种方式的详细实现指南可以参考PSEP 100
传统的信号插槽语法使用了QtCore.SIGNAL和QtCore.SLOT这两个宏来实现,下面这个示例演示了如何使用这种方式:
def someFunc(): print "someFunc has been called!" button = QtGui.QPushButton("Call someFunc") QtCore.QObject.connect(button, QtCore.SIGNAL('clicked()'), someFunc)示例中定义了一个QPushButton,将它点击信号连接到了somFunc这个槽函数上,当我们点击按钮的时候会在控制台输出 someFunc has been called!
新式的信号插槽语法,示例如下:
def someFunc(): print "someFunc has been called!" button = QtGui.QPushButton("Call someFunc") button.clicked.connect(someFunc)这段代码同样实现了上述旧式信号插槽的效果。
信号可以使用QtCore中的Signal类来定义,可以向其中传Python或者C类型作为形参,如果需要重载信号,可以将类型参数放在元组或者列表中。
另外可以在信号参数中可以使用命名的形参方式name来指定信号的名称,如果没有传入name参数,赋值的那个变量名就作为信号的名称。
下面的示例中演示了如何使用QtCore.Signal来定义信号的各种方法:
需要注意的是:信号只能定义在QObject的子类中,定义好的信号会被添加到子类的QMetaObject中。
槽函数通过QtCore.Slot()的装饰器来实现定义和重载,为了定义一个槽函数,你只需要传入一个类型的参数即可。与定义信号不同的是:不可以使用传入列表或者元组的方式来重载槽函数,为了定义不同的槽函数,需要定义新的装饰器。另一个不同之处:Slot同样可以传入一个命名的参数name和result,result可以标识这个函数的返回值类型,name可以设置Slot的名称,如果name没有设置,那么这个槽函数的名称就是它装饰器所装饰的函数。
以下是一些示例程序,主要演示了如何在PySide中使用信号和插槽,包括最简单的方式到相对复杂的方式。
演示了 使用没有形参的信号和插槽
#!/usr/bin/env python import sys from PySide import QtCore, QtGui # define a function that will be used as a slot def sayHello(): print 'Hello world!' app = QtGui.QApplication(sys.argv) button = QtGui.QPushButton('Say hello!') # connect the clicked signal to the sayHello slot button.clicked.connect(sayHello) button.show() sys.exit(app.exec_())
演示了带参数的信号和插槽,使用QtCore.Signal和QtCore.Slot创建自定义信号和插槽
#!/usr/bin/env python import sys from PySide import QtCore # define a new slot that receives a string and has # 'saySomeWords' as its name @QtCore.Slot(str) def saySomeWords(words): print words class Communicate(QtCore.QObject): # create a new signal on the fly and name it 'speak' speak = QtCore.Signal(str) someone = Communicate() # connect signal and slot someone.speak.connect(saySomeWords) # emit 'speak' signal someone.speak.emit("Hello everybody!")
演示了信号和槽函数的重载方法
#!/usr/bin/env python import sys from PySide import QtCore # define a new slot that receives a C 'int' or a 'str' # and has 'saySomething' as its name @QtCore.Slot(int) @QtCore.Slot(str) def saySomething(stuff): print stuff class Communicate(QtCore.QObject): # create two new signals on the fly: one will handle # int type, the other will handle strings speakNumber = QtCore.Signal(int) speakWord = QtCore.Signal(str) someone = Communicate() # connect signal and slot properly someone.speakNumber.connect(saySomething) someone.speakWord.connect(saySomething) # emit each 'speak' signal someone.speakNumber.emit(10) someone.speakWord.emit("Hello everybody!")
演示了定义多个信号的新方式(使用列表的方式)
#!/usr/bin/env python import sys from PySide import QtCore # define a new slot that receives an C 'int' or a 'str' # and has 'saySomething' as its name @QtCore.Slot(int) @QtCore.Slot(str) def saySomething(stuff): print stuff class Communicate(QtCore.QObject): # create two new signals on the fly: one will handle # int type, the other will handle strings speak = QtCore.Signal((int,), (str,)) someone = Communicate() # connect signal and slot. As 'int' is the default # we have to specify the str when connecting the # second signal someone.speak.connect(saySomething) someone.speak[str].connect(saySomething) # emit 'speak' signal with different arguments. # we have to specify the str as int is the default someone.speak.emit(10) someone.speak[str].emit("Hello everybody!")
演示了一个完整的发送信号的方式(将发送信号封装在函数中,可以让外部调用函数的方式)
#!/usr/bin/env python import sys from PySide import QtCore class Communicate(QtCore.QObject): # !!! Must inherit QObject for signals speak = QtCore.Signal() def __init__(self): # !!! Must init QObject else runtime error: PySide.QtCore.Signal object has no attribute ‘emit’ super(Communicate, self).__init__() def speakingMethod(): self.speak.emit() someone = Communicate() someone.speakingMethod()
需要注意的是:信号是类实例动态的对象,不是类的属性,因此不能直接通过类来连接信号和槽函数
翻译文章的原文地址: Signals and Slots in PySide
1. PySide中命名Slot的方式可能是一个Bug,并不能自定义Slot的名称,例如:
@QtCore.Slot(name = "on_pushButton_clicked") def testButton_pressed(self): print "pressed button"按上述文档中的描述应该Slot的名称为on_pushButton_clicked,但在使用中却并不能连接该槽函数。具体可以参考: Named Slot Bug?
2. 注意本文的《新式的信号插槽语法》部分给出的示例,someFunc并没有标识QtCore.Slot,但是仍然能连接上信号和槽函数,事实上Python中的任意函数都可以作为槽函数使用而无需声明为Slot,只要它符合与信号连接的规则(参数对应上),使用QtCore.Slot显示地声明可以提高效率,而不需要Python去隐式地转换类型。具体可以参考:Is the PySide Slot Decorator Necessary?