Publish/Subscribe

#!/usr/bin/env python
#
# [SNIPPET_NAME: Publish/Subscribe]
# [SNIPPET_CATEGORIES: Patterns, PyGTK]
# [SNIPPET_DESCRIPTION: A module providing a minimal implementation of the Publish/Subscribe Pattern]
# [SNIPPET_AUTHOR: Bastian Kennel <[email protected]>]
# [SNIPPET_LICENSE: GPL]

"""This Pattern is very useful for communication between indepentdent parts of a program."""

# wikipedia: http://en.wikipedia.org/wiki/Publish/subscribe

import gtk
import logging

subscriptions = {}

def subscribe(message, subscriber):
    """
    
    Subscribes the subscriber to a message. Subscriber has to be callable and
    accept all parameters the message issues.
    Message can be anything, but should be a primitive (like a string) to not
    complicate subscriber implementations.
    
    """
    if not message in subscriptions:
        subscriptions[message] = [subscriber]
    else:
        subscriptions[message].append(subscriber)
        
def publish(message, *args, **kwargs):
    """
    
    Publish a message with respective arguments.
    Call every subscriber to this message and pass the arguments.
    
    """
    if not message in subscriptions:
        logging.info("Message with no Subscribers: " + str(message))
        return
    for subscriber in subscriptions[message]:
        try:
            subscriber(*args, **kwargs)
        except Exception, e:
            logging.error("Subscriber " + str(subscriber) + " could not handle message " + str(message) + ": " + str(args) + str(kwargs))
            
def unsubscribe(message, subscriber):
    """Unsubscribe the subscriber from the message."""
    if not message in subscriptions:
        logging.info("No Message to unsibscribe from: " + str(message))
    elif not subscriber in subscriptions[message]:
        logging.info("Subscriber " + str(subscriber) + " not subscribed for message " + str(message))
    else:
        subscriptions[message].remove(subscriber)
        
        
# # # # Example Code from here on # # # #

# example taken from the python-snippet 'Buttons'

import pygtk
pygtk.require('2.0')
import gtk

def xpm_label_box(parent, xpm_filename, label_text):
    # Create box for xpm and label
    box1 = gtk.HBox(False, 0)
    box1.set_border_width(2)

    # Now on to the image stuff
    image = gtk.Image()
    image.set_from_file(xpm_filename)

    # Create a label for the button
    label = gtk.Label(label_text)

    # Pack the pixmap and label into the box
    box1.pack_start(image, False, False, 3)
    box1.pack_start(label, False, False, 3)

    image.show()
    label.show()
    return box1
    

# the message to communicate with    
MESSAGE = "THE_message"

# dumb subscription stub
def someFunctionAnywhereDoingAnything(some, data, optional=42):
    print "I received ", some, data, " along with optional ", optional

class Buttons:
    # Our usual callback method
    def callback(self, widget, data=None):
        print "Hello again - %s was pressed" % data
        # publishing
        publish(MESSAGE, "an", " invitation", optional = "nothing.")

    def __init__(self):
        # Create a new window
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)

        self.window.set_title("Image'd Buttons!")

        # It's a good idea to do this for all windows.
        self.window.connect("destroy", lambda wid: gtk.main_quit())
        self.window.connect("delete_event", lambda a1,a2:gtk.main_quit())

        # Sets the border width of the window.
        self.window.set_border_width(10)

        # Create a new button
        button = gtk.Button()

        # Connect the "clicked" signal of the button to our callback
        button.connect("clicked", self.callback, "cool button")

        # This calls our box creating function
        box1 = xpm_label_box(self.window, "info.xpm", "cool button")

        # Pack and show all our widgets
        button.add(box1)

        box1.show()
        button.show()

        self.window.add(button)
        self.window.show()

def main():
    # subscribing
    subscribe(MESSAGE, someFunctionAnywhereDoingAnything)
    gtk.main()
    return 0     

if __name__ == "__main__":
    Buttons()
    main()