PySide中的信号和插槽

本文主要介绍如何在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()自定义信号

信号可以使用QtCore中的Signal类来定义,可以向其中传Python或者C类型作为形参,如果需要重载信号,可以将类型参数放在元组或者列表中。

另外可以在信号参数中可以使用命名的形参方式name来指定信号的名称,如果没有传入name参数,赋值的那个变量名就作为信号的名称。

下面的示例中演示了如何使用QtCore.Signal来定义信号的各种方法:

需要注意的是:信号只能定义在QObject的子类中,定义好的信号会被添加到子类的QMetaObject中。

  • 使用QtCore.Slot()自定义槽函数

槽函数通过QtCore.Slot()的装饰器来实现定义和重载,为了定义一个槽函数,你只需要传入一个类型的参数即可。与定义信号不同的是:不可以使用传入列表或者元组的方式来重载槽函数,为了定义不同的槽函数,需要定义新的装饰器。另一个不同之处:Slot同样可以传入一个命名的参数name和result,result可以标识这个函数的返回值类型,name可以设置Slot的名称,如果name没有设置,那么这个槽函数的名称就是它装饰器所装饰的函数。


  • 示例程序

以下是一些示例程序,主要演示了如何在PySide中使用信号和插槽,包括最简单的方式到相对复杂的方式。

  • 示例1

演示了 使用没有形参的信号和插槽

    #!/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_())

  • 示例2

演示了带参数的信号和插槽,使用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!")
  • 示例3

演示了信号和槽函数的重载方法

    #!/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!")

  • 示例4

演示了定义多个信号的新方式(使用列表的方式)

    #!/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!")

  • 示例5

演示了一个完整的发送信号的方式(将发送信号封装在函数中,可以让外部调用函数的方式)

    #!/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?



你可能感兴趣的:(python,PySide)