Development/Tutorials/Python introduction to signals and slots: Difference between revisions

    From KDE TechBase
    mNo edit summary
    Line 42: Line 42:


    ==Signals and slots with parameters==
    ==Signals and slots with parameters==
    The signal and slots mechanism is type safe. In C++ this implies that both the number of arguments and the type of the arguments in a signal must match the arguments in the receiving slot. The term is a bit unclear when dealing with Python because of it's dynamically typed nature.  
    The signal and slots mechanism is type safe. In C++ this implies that both the number of arguments and the type of the arguments in a signal must match the arguments in the receiving slot. The receiving slot can actually have fewer parameters than the emitted signal, the extra arguments will then be ignored. The term type safe is a bit unclear when dealing with Python objects because of it's dynamically typed nature.  


    If we connect custom made signals to a Qt built in slots we have to make sure that parameters is of the correct type or that they can be automatically converted to the correct type. For example a python string will automatically be converted to a QString. If we send a object of an incompatible type we will get an runtime error. It's possible to send a python object of any type using PyQt_PyObject in the signature. This is  recommended when both signal and slot is implemented in python. By using PyQt_PyObject we avoid unnecessary conversions between python objects and C++ types and  it more consistent with python dynamically typed nature.
    When connecting to Qt Built in slots we have to make sure that parameters is of the correct type or that they can be automatically converted to the correct type. For example a python string will automatically be converted to a QString. If we send a object of an incompatible type we will get an runtime error.


     
    '''Example'''
    '''Example 1'''
    <code python>
    <code python>
    from PyQt4.QtGui import *
    from PyQt4.QtGui import *
    Line 70: Line 69:
    </code>
    </code>


    ==Python objects as parameters==
    It's possible to send a python object of any type using PyQt_PyObject in the signature. This is recommended when both signal and slot is implemented in python. By using PyQt_PyObject we avoid unnecessary conversions between python objects and C++ types and it more consistent with python dynamically typed nature.
    '''Example'''
    <code python>
    import sys
    from time import time
    from PyQt4.QtCore import *
           
    class A (QObject):
        def __init__(self):
            QObject.__init__(self)
       
        def send (self):
            msg=[1234,"1234",{1:2}]
            self.emit(SIGNAL("asignal(PyQt_PyObject)"),msg)   
           
        def recive(self,msg):
            print msg
    def p(msg): print int(time()-start),msg
           
    if __name__=="__main__":
        app=QCoreApplication(sys.argv)
        a=A()
        QObject.connect(a,SIGNAL("asignal(PyQt_PyObject)"),a.recive)
        a.send()   
       
        sys.exit(app.exec_())
    </code>
    ==Short-circuit Signal==
    PyQt4 have a special type of signal called a short-circuit Signal. This signal implicitly declare all arguments to be of type PyQt_PyObject. Short-circuited signals does not have argument lists or parentheses. Short-circuited signals can only connected to python slots.
    '''The same example as above, using short-circuited signals. '''
    <code python>
    import sys
    from time import time
    from PyQt4.QtCore import *
           
    class A (QObject):
        def __init__(self):
            QObject.__init__(self)
       
        def send (self):
            msg=[1234,"1234",{1:2}]
            self.emit(SIGNAL("asignal"),msg)   
           
        def recive(self,msg):
            print msg
    def p(msg): print int(time()-start),msg
           
    if __name__=="__main__":
        app=QCoreApplication(sys.argv)
        a=A()
        QObject.connect(a,SIGNAL("asignal"),a.recive)
        a.send()   
       
        sys.exit(app.exec_())
    </code>


    ==Creating custom signals==
    ==Creating custom signals==

    Revision as of 18:48, 10 January 2007

    Warning
    This tutorial is work in progress.


    Abstract

    The signal and slot architecture is design to simplify communication between objects. It's a fact that GUI programming is mostly event driven. The traditionally approach to event driven programming is to use callbaks. Callbacks have a number of limitations that Qt tries to solve with it's signal and slot architecture. The concept is that every object can emit signals. For example when a button is clicked it emits a “clicked()” signal. Signals does not do anything alone, but when connected to a slot the code in the slot will be executed every time the signal is emitted. In python every function is a slot. It's possible to connect one signal to multiple slots


    Prerequisites

    General understanding of the python programming language. No prior knowledge of QT is required.


    Connecting signals and slots.

    We use the QObject.connect() method to connecting signals and slots.

    bool connect (QObject, SIGNAL(), callable, Qt.ConnectionType = Qt.AutoConnection)

    The first argument is the name of the object that is emitting the signal. The second argument is the signal, and the third argument is the SLOT. The third argument has to bee a python callable object.


    This example demonstrates how to connect signals and slots.

    from PyQt4.QtGui import * from PyQt4.QtCore import * import sys

    1. First we create a QApplication and QPushButton

    app=QApplication(sys.argv) exitButton=QPushButton("Exit")

    1. Her we connect the exitButton's "clicked()" signals to the app's exit method.
    2. This will have the effect that every time some one clicks the exitButton the app.exit method will execute and the application will close.

    QObject.connect(exitButton,SIGNAL("clicked()"),app.exit)

    exitButton.show()

    1. Start the evnt loop

    sys.exit(app.exec_())


    Signals and slots with parameters

    The signal and slots mechanism is type safe. In C++ this implies that both the number of arguments and the type of the arguments in a signal must match the arguments in the receiving slot. The receiving slot can actually have fewer parameters than the emitted signal, the extra arguments will then be ignored. The term type safe is a bit unclear when dealing with Python objects because of it's dynamically typed nature.

    When connecting to Qt Built in slots we have to make sure that parameters is of the correct type or that they can be automatically converted to the correct type. For example a python string will automatically be converted to a QString. If we send a object of an incompatible type we will get an runtime error.

    Example from PyQt4.QtGui import * from PyQt4.QtCore import * import sys

    def printNumber(number):

       print number
    

    if __name__=="__main__":

       #First we create a QApplication and QPushButton
       app=QApplication(sys.argv)
       slider=QSlider(Qt.Horizontal)
       
       
       QObject.connect(slider,SIGNAL("valueChanged(int)"),printNumber)
       
       
       slider.show()
       #Start the evnt loop
       sys.exit(app.exec_())
    


    Python objects as parameters

    It's possible to send a python object of any type using PyQt_PyObject in the signature. This is recommended when both signal and slot is implemented in python. By using PyQt_PyObject we avoid unnecessary conversions between python objects and C++ types and it more consistent with python dynamically typed nature.

    Example import sys from time import time from PyQt4.QtCore import *


    class A (QObject):

       def __init__(self):
           QObject.__init__(self)
       
       def send (self):
           msg=[1234,"1234",{1:2}]
           self.emit(SIGNAL("asignal(PyQt_PyObject)"),msg)    
           
       def recive(self,msg):
           print msg
    

    def p(msg): print int(time()-start),msg

    if __name__=="__main__":

       app=QCoreApplication(sys.argv)
       a=A()
       QObject.connect(a,SIGNAL("asignal(PyQt_PyObject)"),a.recive)
       a.send()    
       
       sys.exit(app.exec_())
    


    Short-circuit Signal

    PyQt4 have a special type of signal called a short-circuit Signal. This signal implicitly declare all arguments to be of type PyQt_PyObject. Short-circuited signals does not have argument lists or parentheses. Short-circuited signals can only connected to python slots.

    The same example as above, using short-circuited signals. import sys from time import time from PyQt4.QtCore import *


    class A (QObject):

       def __init__(self):
           QObject.__init__(self)
       
       def send (self):
           msg=[1234,"1234",{1:2}]
           self.emit(SIGNAL("asignal"),msg)    
           
       def recive(self,msg):
           print msg
    

    def p(msg): print int(time()-start),msg

    if __name__=="__main__":

       app=QCoreApplication(sys.argv)
       a=A()
       QObject.connect(a,SIGNAL("asignal"),a.recive)
       a.send()    
       
       sys.exit(app.exec_())
    

    Creating custom signals

    Signals and slots and python objects

    Signals and slots and threading

    To send signal across threads we have to use the Qt.QueuedConnection parameter. Without this parameter the code will be executed in the same thread. import sys from time import time from PyQt4.QtCore import *

    class A (QThread):

       def __init__(self):
           QThread.__init__(self)
       
       def afunc (self):
           p("starting in a()")
           self.emit(SIGNAL("asignal"))
           p("finished in a()")
          
       def bfunc(self):
           p("starting in b()")
           self.sleep(3) 
           p("finished in b()")
                   
       def run(self):
           self.exec_()
           
    
    

    def p(msg): print str(int(time()-start)) + "s",msg

    if __name__=="__main__":

       start=time()
       p("starting in __main__")
       app=QCoreApplication(sys.argv)
       a=A()
       a.start()
       QObject.connect(a,SIGNAL("asignal"),a.bfunc,Qt.QueuedConnection)
       a.afunc()
       p("finished in __main__")
          
       sys.exit(app.exec_())
    

    Output: 0s starting in __main__ 0s starting in a() 0s finished in a() 0s finished in __main__ 0s starting in b() 3s finished in b()


    without Qt.QueuedConnection the example will output: 0s starting in __main__ 0s starting in a() 0s starting in b() 3s finished in b() 3s finished in a() 3s finished in __main__