Building event driven systems

The ocempgui.events module provides a small and fast event management system. It is currently separated into three different classes, of which the most important is the EventManager class. Besides the EventManager, an Event class for sending event data and an EventCallback class for connecting functions or methods to signals are available.

To create an own event driven application system or to enhance an existing application, only a few guidelines have to be respected and only a minimal set of changes be made on existing code. You will need to

To understand, what you are doing and to know the pitfalls of the event management system, you first have to know, how it works. The next subsection will give you a short explanation of it.

How the event management works

The event management system of OcempGUI uses a simple approach using signal slots. This means, that objects will register themselves only for specific event types, of which they want to be notified. Any other event will not be sent to them.This reduces the overhead of events the objects have to deal with (either by dropping or processing them) and improves the performance and scalability of the event management system (especially with many objects).

Event management diagram.

Figure 1. Event management


Enabling objects to receive events

An object, which shall be event aware, should inherit from the INotifyable class and implement its notify() method, which will receive events distributed by the EventManager. The signature of the method looks like the following:

class OwnObject (INotifyable):
    ...
    def notify (self, event):
        ...
      

Note

Your classes do not need to explicitly inherit from INotifyable, but have to implement the notify() method with its correct signature.

The event argument of the method will be an Event object, which can be used to perform certain actions within the method body then:

class OwnObject (INotifyable):
    ...
    def move (self, coords):
       # Moves the object to the desired coordinates (x, y).
       self.x = coords[0]
       self.y = coords[1]
       print "Moved to %d,%d" % (self.x, self.y)

    def notify (self, event):
        # Check the event signal and run a certain action with its data.
        if event.signal == "clicked":
           print "Something was clicked!"
        elif event.signal == "move":
           # Assuming that the event.data contains a coordinate tuple.
           self.move (event.data)
        

Example 12. Enabling an object to receive events


Setting up the event management

Setting up the main event management is nearly as easy as enhancing the objects. To add objects to the EventManager, the add_object() method has to be invoked. It receives the object to add and a list of signal ids as arguments. The signal ids will cause the object to be registered in specific queues, to which events with matching signal ids then will be sent.

Objects can be removed from the EventManager using the remove_object() method. The method allows you to either remove the object from specific slots or from all slots, it is registered for, at once.

We use the OwnObject class of the previous example and will (un)register it for the signals "move" and "clicked".

# Create an EventManager and OwnObject instance.
manager = EventManager ()
myobj = OwnObject ()

# Add the object to the EventManager.
manager.add_object (myobj, "move", "clicked")

# Remove the object from the 'clicked' slot.
manager.remove_object (myobj, "clicked")

# Remove the object from _all_ slots it still listens on.
manager.remove_object (myobj)
        

Example 13.  Adding and removing an object to the EventManager


Sending events

Now let us proceed to the most important: sending events. To send events to the objects of the EventManager, you can use the emit() method. It receives two arguments, which will become the signal and data of a Event object. The Event will be created by the EventManager and then sent to the matching objects. So all you have to do is to pass the emit() method the correct information for the event.

Both arguments the emit() receives, have no limitations of type, length or whatsoever. It is up to you to to send correct information through the event management system and to check for correct information on the object side.

# Send events to the registered objects via the emit() method.
manager.emit ("clicked", None)
manager.emit ("move", (10, 10))
        

Example 14.  Sending events through the EventManager


Complete event management example

The following example is a complete example based on the excerpts from above. You can find it as python script under examples/eventmanager.py

# EventManager usage example.
from ocempgui.events import EventManager, INotifyable

# Create a new event capable object. This can be acquired by adding a
# 'notify ()' method to the object, which receives a single argument.
class OwnObject (INotifyable):
    def __init__ (self):
        self.x = 0
        self.y = 0
    
    def move (self, coords):
       # Moves the object to the desired coordinates (x, y).
       self.x = coords[0]
       self.y = coords[1]
       print "Moved to %d,%d" % (self.x, self.y)

    def notify (self, event):
        # Check the event signal and run a certain action with its data.
        if event.signal == "clicked":
           print "Something was clicked!"
        elif event.signal == "move":
           # Assuming that the event.data contains a coordinate tuple.
           self.move (event.data)

# Create an EventManager and OwnObject instance.
manager = EventManager ()
myobj = OwnObject ()

# Add the object to the EventManager.
manager.add_object (myobj, "move", "clicked")

# Send events to the registered objects via the emit() method.
manager.emit ("clicked", None)
manager.emit ("move", (10, 10))

# Remove the object from the 'clicked' slot.
manager.remove_object (myobj, "clicked")

# Send the 'clicked' event once more.
manager.emit ("clicked", None)

# Remove the object from _all_ slots it still listens on.
manager.remove_object (myobj)

# Send the 'move' event again.
manager.emit ("move", (40, 40))

Example 15. Complete event management example