Connecting signals and slots

If you have worked with pointers in C++ or object references in other languages, then you will probably have been wondering whether creating signal/slot connections also means creating object references. Remember, object A can register interest in some signals Object B emits.

This means that somewhere there must be code that object B calls when it wants to emit that signal; and that there must be code that is called to notify object A. In order for that to be possible, a reference must be stored to object A. This is known as the observer pattern:

Example 9-9. sigslot.py - a simple signals/slots implementation in Python, following the Observer pattern

#
# sigslot.py - a simple signals/slots implementation in Python
#

class ClassA:

    def __init__(self):
        self.interestedObjects=[]


    def connect(self, obj):
        self.interestedObjects.append(obj)

    def sendSignal(self):
        for obj in self.interestedObjects:
            obj.slot("This is a signal from ClassA")

class ClassB:

    def slot(self, message):
        print "Object with ID", id(self), "Got signal: message"

objectA=ClassA()
objectB=ClassB()
objectA.connect(objectB)
objectC=ClassB()
objectA.connect(objectC)
objectD=ClassB()
objectA.connect(objectD)

objectA.sendSignal()
      

In this exceedingly simplified implementation of the signals and slots concept, objectA actually stores the references to interested objects. If the PyQt signals and slots mechanism were implemented like this, objects would not be deleted unless the all objects they had connections to were deleted as well. This puts a burden on the programmer, who would have to remember to sever all connections by hand. We all know what happens when a programmer has to remember cleaning up after him...

Fortunately, the implementation of signals and slots in sip is not quite so basic. Sip works together with the signals and slots implementation in Qt, which is highly complex, and involves fooling around with a special-purpose macro processor. This, at least, Python developers are spared.

Sip keeps special proxy objects around to handle the signal/slot connections. If you use a recent version of Python (>2.1), the actual connections will not need real references, but can work with the new-fangled weak reference concept. Weak references are references that don't count for the purpose of reference counting.

This is good, because your application will not crash if a signal is emitted that was connected to a slot in a deleted object — and created connections will not keep objects alive.

Chapter 7 deals with signals and slots in far more depth.