Observer patterns are quite useful in many circumstances. As the name implies, such a pattern notifies an “observer” class about some change in another class, often called the “subject”. In Python it is very straightforward to implement such a pattern and the following code snippet -which I use quite often in my everyday programming- shows a possible way to do it:
import weakref class Subject: def __init__(self): self._observers = [] self.isNotifying = False def attach(self, observer): r = weakref.ref(observer) if not r in self._observers: self._observers.append(r) def setObservers(self,observers): if not observers == None: self._observers = observers else: self._observers = [] def observers(self): return self._observers def detach(self, observer): r = weakref.ref(observer) try: if DEBUG: print "Removing observer." self._observers.remove(r) except ValueError: print "Could not remove observer %s" % str(r()) def notify(self,property = None, value = None,modifier = None): try: #This is to avoid infinite notification loop, e.g. when the notified class calls a function of the subject that triggers another notify and so on... if self.isNotifying: print "WARNING: notify was called recursively, aborting." return False self.isNotifying = True for observer in self._observers: if observer() == None: self._observers.remove(observer) elif modifier != observer(): observer().updated(self,property,value) self.isNotifying = False except: print "An error occured when notifying my observers..." print sys.exc_info() self.ifNotifying = False raise #The corresponding observer class. It makes sure that the observing class has an "updated" function. class Observer: def updated(self,subject = None,property = None,value = None): pass
And here’s how to use it:
class MySubject(Subject): def __init__(self): Subject.__init__(self) def addNumbers(self,a,b): #Do something.... c = a+b self.notify("addNumbersResult",c) class MyObserver(Observer): #... def updated(self,subject = None,property = None,value = None): if property == "addNumbersResult": print "Addition complete. Result: %g" % value observer = MyObserver() subject = MySubject() subject.attach(observer) subject.addNumbers(1,2)
This will print ‘Addition complete: 3′.
Several things in the code above are worth noting:
- First, the Subject class stores only a weak reference to the observer. If we would use a normal reference instead, an observer would not be deleted (and thus garbage-collected) without explicitly detaching it from the subject, which in general is not what we want.
- The “notify” function contains a safeguard that makes sure that it never gets called recursively. This helps to avoid infinite loops which one can produce rather easily by e.g. having an observer which always triggers another “notify” event while being notified itself. This behaviour might not always be exactly what you want, so feel free to remove this feature if you don’t need it.
This simple pattern is both versatile and very easy to use. Nevertheless, it can be dangerous to use it in a GUI application, especially when multi-threading is involved. This is because most GUI toolkits -such as for example Qt- do not allow you to call any GUI-relevant function from a thread other than the main (GUI-) thread. Ignoring this restriction can yield a lot of unpleasant surprises and will often break your program. Suppose for example that you have a “Subject” class running in a worker thread and that this class notifies some GUI component. Now, if you would call any GUI-related function during the notification process (which gets executed in the worker thread) you will most likely produce some very unpredictable and unpleasant behaviour.
This problem can be circumvented if we introduce an observer class that does not process the “notify” directly upon reception but rather queues it and processes it later from the main (GUI-) thread. The following Python class does exactly this by using a timer function and an event queue. It is designed for use with Qt but can be easily generalized to work with other GUI toolkits as well.
#This is an observer class which is especially tailored to Qt widgets. Instead of processing "updated" calls directly, #this class queues them and periodically calls a function from the main thread that processes the events. class ObserverWidget(Observer): #This function gets called from the notifying (possibly non-GUI) thread. #It is NOT safe to call GUI-related functions from this function. def updatedThread(self,subject = None,property = None,value = None): pass #This function gets called from the main (GUI) thread. #It is safe to call GUI-related functions from this function. def updatedGui(self,subject = None,property = None,value = None): pass #We intercept the "update" call from the subject and queue it. def updated(self,subject = None,property = None,value = None): self.updatedThread(subject,property,value) self.updateQueue.append([subject,property,value]) #This function periodically processes the update queue. def processUpdateQueue(self): while len(self.updateQueue) > 0: event = self.updateQueue.pop(0) self.updatedGui(*event) def clearUpdateQueue(self): self.updateQueue = [] def __init__(self,parent = None): self.updateQueue = [] self.updateQueueTimer = QTimer() self.updateQueueTimer.setInterval(100) self.connect(self.updateQueueTimer,SIGNAL("timeout()"),self.processUpdateQueue) self.updateQueueTimer.start()
To use this pattern you just inherit from “ObserverWidget” and use the functions “updatedThread” (for everything that can be done in the “Subject” thread) and “updatedGui” (for everything that should be done in the main thread) instead of “updated” to process your event notifications. If you use this class, please make sure that the class which you derive from it also derives from QWidget (or a child class of it) and that you call the constructor of this GUI class BEFORE calling the constructor of the ObserverWidget.