Orange Widgets Reference Guide for Developers

Channels Definitions, Data Exchange

Input and output channels are defined anywhere within the __init__ function of a main widget class. The definition is used when running a widget, but also when registering your widget within Orange Canvas. Channel definitions are optional, depending on what your widget does.

Output Channels

Following is an example that defines two output channels:

self.outputs = [("Sampled Data", orange.ExampleTable),
                ("Learner", orange.Learner)]

self.outputs should thus be a list of tuples, within each the first element is a name of the channel, and the second the type of the tokens that will be passed through. Token types are class names; most often these are some Orange classes, but they can also be anything you may define as class in Python.

Widgets send the data by using self.send call, like:

self.send("Sampled Data", mydata)

Parameters of send are channel name and a token to be send (e.g., a variable that holds the data to be send through the channel).

When tokens are send around, the signaling mechanism annotates them with a pointer to an object that sent the toke (e.g., a widget id). Additionally, this annotation can be coupled with some name passed to send, in case you have a widget that can send few tokens one after the other and you would like to enable a receiving widget recognize these are different tokens (and not updates of the same one):

id = 10
self.send("Sampled Data", mydata, id)

Input Channels

An example of the simplest definition of an input channel is:

self.inputs = [("Data", orange.ExampleTable, self.receiveData)]

Again, self.inputs is a list of tuples, where the elements are the name of the channel, followed by a channel type and a Python function that will be called with any token received. For the channel defined above, a corresponding receiving function would be of the type (we would most often define it within the widget class defintion, hence self for the first attribute):

def receiveData(self, data):
    # handle data in some way

Any time our widget would receive a token, receiveData would be called. Notice there would be no way of knowing anything about the sender of the token, hence widget would most often replace the previously received token with the new one, and forget about the old one.

Widgets can often clear their output by sending a None as a token. Also, upon deletion of some widget, this is the way that Orange Canvas would inform all directly connected downstream widgets about deletion. Similar, when channels connecting two widgets are deleted, Orange Canvas would automatically send None to the receiving widget. Make sure your widget handles None tokens appropriately!

There are cases when widget would like to know about the origin of a token. Say, you would like to input several learners to the evaluation widget, how would this distinguish between the learners of different origins? Remember (from above) that tokens are actually passed around with IDs (pointers to widgets that sent them). To declare a widget is interested about these IDs, one needs to define an input channel in the following way:

self.inputs = [("Learners", orange.Learner, self.learner, Multiple)]

where the last argument refers if we have a “Single” (default if not specified) or a “Multiple” channel. For the above declared channel, the receiving function should include an extra argument for the ID, like:

def learner(self, learnertoken, tokenid):
    # handle learnertoken and tokeid in some way

Widgets such as OWTestLearners and alike use such schema.

Finally, we may have input channels of the same type. If a widget would declare input channels like:

self.inputs = [("Data", orange.ExampleTable, self.maindata),
               ("Additional Data", orange.ExampleTable, self.otherdata)]

and we connect this widget in Orange Canvas to a sending widget that has a single orange.ExampleTable output channel, Canvas would bring up Set Channels dialog. There, a sending widget’s channel could be connected to both receiving channels. As we would often prefer to connect to a single (default) channel instead (still allowing user of Orange Canvas to set up a different schema manually), we set that channel as the default. We do this by the using the fourth element in the channel definition list, like:

self.inputs = [("Data", orange.ExampleTable, self.maindata, Default),
               ("Additional Data", orange.ExampleTable, self.otherdata)]