PubSub within wx.lib
Contents
Introduction
The wx.lib.pubsub module provides a publish-subscribe broker that allows parts of your application to broadcast messages of a given topic to other parts of the application. Listeners can be any callable objects in your application, ie functions, bound methods, any object with a call, etc. Note that pubsub itself does not broker over a network, but only within an application.
The module is well documented and you'll find most of what you need at pubsub's home page.
History
Before wxPython 2.8.11 (Spring 2010), wx.lib.pubsub was one module, called the "original" pubsub or "pubsub version 1". Pubsub was given its own home, independent of wxPython, around 2007, becoming PyPubSub. It was rewritten to provide a more generic API that better supports customization of exception handlers, notifications, topic tree specification, and a more expressive message data signature via named arguments. The new API, called "version 3", is not backwards compatible with version 1. However wx.lib.pubsub remained the original pubsub implementation for several years.
Starting with wxPython 2.8.11, wx.lib.pubsub integrated the new PyPubSub. In wxPython 2.8.11, wx.lib.pubsub is configured to expose PyPubSub's "version 1" API rather than the newer, more capable version 3 API. This will give pubsub users a chance to upgrade their wxPython-based application to pubsub version 3 if and when they are ready. To use the new version 3 API in wxPython >= 2.8.11.0, an application must specifically request it via a configuration module, discussed later.
Pubsub development is now hosted at http://sourceforge.net/projects/pubsub|SourceForge]] as PyPubSub (though for reasons too long to explain the sourceforge project name is "pubsub" rather than "pypubsub") and is standalone, ie it does not require wxPython to be used. Therefore, developers still using versions of wxPython older than 2.8.11.0 but wishing to use the new pubsub version 3 API can download PyPubSub from SF.net and install it as a standalone Python package.
Example using API version 1
The wx.lib.pubsub is easy to use. Here is a small example;
1 from wx.lib.pubsub import Publisher as pub
2
3 class SomeReceiver(object):
4 def __init__(self):
5 # here we connect to a signal called 'object.added'
6 # we use dotted notation for more specialized topics
7 # but you can use tuples too
8 pub.subscribe(self.__onObjectAdded, 'object.added')
9
10 def __onObjectAdded(self, message):
11 # data passed with your message is put in message.data.
12 # Any object can be passed to subscribers this way.
13 print 'Object', message.data, 'is added'
14
15 # usage;
16
17 a = SomeReceiver()
18
19 pub.sendMessage('object.added', 'HELLO WORLD')
20
21 # Now our object receives the message, with the data being a string
If you have used PyDispatcher, the concept is different, as the signals are integers rather than topic strings. Note that the tuple form of a topic name works too:
This is in general less readable but can be useful when parametrizing a topic name.
Note also that these sendMessage calls may be placed anywhere, which gives great flexibility/ Now, classes don't need to "know" about each other, which reduces inter-dependencies and therefore coupling. This can help to eliminate spaghetti code and is an excellent way to create Model View Controller based applications.
The New PubSub: version 3
The new PubSub requires keyworded arguments for message data, making the code more explicit (self-documenting), and reduces the likelyhood of data mismatches (where you send data that is not expected or can't be handled by listeners of message topic). This is demonstrated below (assuming pubsub is installed to your python site-packages)
1 # first line below is necessary only in wxPython 2.8.11.0 since default
2 # API in this wxPython is pubsub version 1 (expect later versions
3 # of wxPython to use the kwargs API by default)
4 from wx.lib.pubsub import setupkwargs
5
6 # regular pubsub import
7 from wx.lib.pubsub import pub
8
9 class SomeReceiver(object):
10 def __init__(self):
11 pub.subscribe(self.__onObjectAdded, 'object.added')
12
13 def __onObjectAdded(self, data, extra1, extra2=None):
14 # no longer need to access data through message.data.
15 print 'Object', repr(data), 'is added'
16 print extra1
17 if extra2:
18 print extra2
19
20
21 a = SomeReceiver()
22 pub.sendMessage('object.added', data=42, extra1='hello!')
23 pub.sendMessage('object.added', data=42, extra1='hello!', extra2=[2, 3, 5, 7, 11, 13, 17, 19, 23])
See Also
Good discussion on publish/subscribe architecture in general
Explanation of Coupling and its impact on clarity, maintainability and extendability