The communication framework

Publish/Subscribe model

Glue is built around a publish/subscribe paradigm that allows individual components to remain synchronized without knowing about each other. The core object that allows this is the Hub, which listens for messages from various parts of Glue and relays messages to other interested objects about changes in state to the data and subsets.

You can instantiate a Hub instance directly:

>>> from glue.core import Hub
>>> hub = Hub()

but in most cases if you are using a DataCollection, you can let it instantiate the hub instead and access it via the .hub attribute:

>>> from glue.core import DataCollection
>>> data_collection = DataCollection()
>>> data_collection.hub
<glue.core.hub.Hub at 0x102991dd8>

Messages are exchanged using Message objects. A message is a notice that something interesting has happened. Various sub-classes of Message exist, such as DataMessage or SubsetMessage, and even more specialized ones such as DataCollectionAddMessage.

Using the subscribe() method, you can easily attach callback functions/methods to specific messages using the syntax:

hub.subscribe(self, subscriber, message_class, handler=..., filter=...)

where the message_class is the type of message to listen for, such as DataMessage, handler is the function/method to be called if the message is received (the function/method should take one argument which is the message), and filter can be used to specify conditions in which to pass on the message to the function/method (for more information on this, see the subscribe() documentation).

Subscribing to messages has to be done from a HubListener instance. The following simple example shows how to set up a basic HubListener and register to listen for DataMessage and DataCollectionAddMessage:

>>> from glue.core import Hub, HubListener, Data, DataCollection
>>> from glue.core.message import (DataMessage,
...                                DataCollectionMessage)
>>> class MyListener(HubListener):
...     def __init__(self, hub):
...         hub.subscribe(self, DataCollectionMessage,
...                       handler=self.receive_message)
...         hub.subscribe(self, DataMessage,
...                       handler=self.receive_message)
...     def receive_message(self, message):
...         print("Message received:")
...         print("{0}".format(message))

We can then create a data collection, and create an instance of the above class:

>>> data_collection = DataCollection()
>>> hub = data_collection.hub
>>> listener = MyListener(hub)

If we create a new dataset, then add it to the data collection created above, we then trigger the receive_message method:

>>> data = Data(x=[1,2,3])
>>> data_collection.append(data)
Message received:
    Sent from: DataCollection (1 data set)

Note that DataCollectionAddMessage is a subclass of DataCollectionMessage – when registering to a message class, sub-classes of this message will also be received.

It is also possible to trigger messages manually:

>>> # We can also create messages manually
... message = DataMessage(data)
>>> hub.broadcast(message)
Message received:
     Sent from: Data Set: Number of dimensions: 1
Shape: 3
 0) x
 1) Pixel Axis 0
 2) World 0

Typical workflow

This is used in Glue to produce the following communication workflow:

  • An empty DataCollection object is created, and automatically connected to a Hub.
  • Data are added to the data collection
  • Several clients register to the hub, and subscribe to particular types of messages.
  • Something (perhaps code, perhaps user interaction with a client) acts to change the state of a data or subset object. These changes automatically generate particular messages that get sent to the Hub. These messages communicate atomic events such as a change in the data, a change in a subset, or the fact a subset has been deleted.
  • Upon receiving a message, the hub relays it to all clients that have subscribed to that particular message type.
  • The clients react to the message however they see fit.

Here, we use the term client in the generic sense of a class that interacts with the hub. However, Glue does include a base Client class that pre-defines a number of useful connections for data viewers. Some of the data viewers make use of this class, although there is no obligation to do so in principle, provided the class subscribing to messages is a subclass of HubListener.