Boing enables to create pipelines for connecting different input sources to multiple target destinations (e.g. applications, logs, etc.) and eventually process the data before being dispatched.
As an example, consider the pipeline in figure 1.1: two tactile devices (left side) are connected to a single user application (top-right). At the same time, the contact events from both the devices are forwarded as a JSON stream to a second remote application (e.g. a contact visualiser), while an event recorder is used to log into a file the data stream provided by the second device only.
Figure 1.1: Example of pipeline created using Boing.
Even if the tactile devices provides different data structures, Boing enables to merge them in a single data stream (in this example the TUIO and the JSON stream). Contact events are also processed before being passed to the application: Boing provides nodes to smooth or calibrate the input data (e.g. position, speed, etc.). As shown in the example, pipelines can be composed by parallel branches so that each input/output can have its own processing suite.
Boing does not impose a specific data model; instead it exploits a query path language (similar to JSONPath) for accessing the data to be processed, so that it can fit a wide range of application domains.
The current version is 0.3.1, released on September 18, 2012. Read the ChangeLog.
Download one of the following depending on your platform:
File | Type | Py Version | Size |
---|---|---|---|
boing-0.3.1.tar.gz | Source | 3.2 | 702KB |
boing-0.3.1.zip | Source | 3.2 | 833KB |
boing-0.3.1.win32.msi | MS Windows MSI installer | 3.2 | 544KB |
If you are interested to the development version, you can clone the source repository. Open a terminal and type:
git clone https://github.com/olivopaolo/boing
Boing requires the Python 3.2 interpreter (or newer) and the PyQt4 package (a set of Python bindings for Nokia’s Qt application framework). Moreover, it also requires the following Python packages:
Extensive installation instructions are available for the following platforms:
It is possible to install both Python and PyQt directly from the standard repositories by typing in a terminal:
sudo apt-get install python3-pyqt4 python3-setuptools python3-numpy
In order to complete the installation, open a terminal and type:
cd <BOING-DIRECTORY>
sudo python3.2 setup.py install
If you have Mac Ports, getting PyQt4, numpy and Distribute is as simple as typing:
sudo port install py32-pyqt4 py32-distribute py32-numpy
In order to complete the installation, open a terminal and type:
cd <BOING-DIRECTORY>
sudo python3.2 setup.py install
The boing executable may be not installed into a directory
indexed by the PATH
variable, so that it is always necessary
to use the full path to launch it. To avoid this annoying behaviour, a
simple solution is to set the installer target directory using the option
--install-scripts
. As an example:
sudo python3.2 setup.py install --install-scripts /usr/local/bin
First download the Python 3.2 binary installer and run it.
After Python has been installed, it may be useful to modify the PATH environment variable, so that Windows can find Python binaries and scripts without the need of specifing all the times the entire path. As an example, the PATH variable has been set to:
PATH = C:\Python32;C:\Python32\Scripts
The package Distribute is necessary to run the boing’s installer script. Download the file distribute_setup.py and type in a terminal:
cd <DOWNLOAD-DIRECTORY>
python distribute_setup.py
Download the numpy binary installer and run it.
If you are running a Windows 7 64 bit, you may also use the unofficial numpy Windows binary installer that you can find at http://www.lfd.uci.edu/~gohlke/pythonlibs
Download the source code of pyparsing, extract the archive and type in a terminal:
cd <PYPARSING-DIRECTORY>
python setup.py install
If you downloaded the binary installer you just have to launch it, otherwise extract the source archive and type in the terminal:
cd <BOING-DIRECTORY>
python setup.py install
After the installation has been completed, it may be useful to run the test suite in order to verify that everything has been correctly installed. In order to do so, type in a terminal:
python3 setup.py test
It is also possible to test only a subset of the Boing‘s modules:
cd <BOING-DIRECTORY>
python3 boing/test/run.py [MODULE [MODULE ...]]
The available modules are: core
, filtering
,
gesture
, net
, nodes
, utils
. If no
module is specified, all the available modules will be tested.
If you are interested to check the code coverage, you may use the tool called coverage by Ned Batchelder. Once the tool has been installed, you simply have to type:
cd <BOING-DIRECTORY>
coverage run --source boing boing/test/run.py
coverage report -m
This section contains various tutorials to easily learn to use the toolkit Boing:
This tutorial provides few simple examples of the functionality of the toolkit to help you starting to use the Boing toolkit.
Let’s consider to have a multi-touch input device, like a tablet or a touch-screen. What cool things can I do with Boing? Boing enables to create a pipeline for connecting your device to different targets, like applications, frameworks, debuggers and eventually processing the gesture events before having being dispatched, like for example calibrate the contacts’ position or apply a smoothing filter.
To make things easier, let’s consider that your device can send the information of the contact events as a TUIO stream on the local port 3333. [1]
First of all, it is important to know that all the Boing‘s tools are invoked by using the script boing. Open a terminal and type:
boing "in.tuio://:3333 + viz:"
The script should have opened a window displaying a grid. Now when you touch your multi-touch device, you will be able to see the contact points appear on the window.
It’s not difficult to notice that the script accepts a single argument that defines the configuration of the pipeline that is to be created. Configurations are defined by a formula where the operands define the functionality of the nodes of the pipeline, while the operators define how the nodes are connected, therefore also the structure of the pipeline.
In the previous example, the pipeline was composed by two nodes:
in.tuio://:3333
corresponds to a node that reads the socket,
decodes the TUIO stream and provides the multi-touch events;viz:
corresponds to the Contact Visualizer, a widget
that shows the information of the contact points, such as position,
track and speed.The two nodes are joined using the +
operator, which stands
for connection in series. The structure of the pipeline is
represented in figure 3.1.
Figure 3.1: Pipeline obtained from the configuration
in.tuio://:3333 + viz:
.
Congratulations! You have created your first Boing pipeline!
Now, let’s try new functionalities by adding a new node. Stop the previous pipeline by closing the visualizer widget or pressing Ctrl-C on the terminal, and type in the terminal:
boing "in.tuio://:3333 + (viz: | dump:)"
As before the contact visualizer appears again, but this time, when
you touch the multi-touch device, the terminal prints a lot of data!
The terminal output represents all the data that the in.tuio://:3333
node can produce and send to the connected nodes. This tutorial is not
aimed to provide an exaustive description of the message structure;
for the moment, simply observe that data messages are hierarchical
structures mainly composed by Python built-in types, such as
dictionaries, lists, strings, bytearrays, etc. Thanks to such standard
structure, Boing exploits a query language, similar to JSONPath,
for the indexing or the filtering of data messages. In order to
understand the usefulness of such query language, stop the pipeline
and type in the terminal:
boing "in.tuio://:3333 + (viz: | dump:?request=..contacts)"
Now, when you touch your multi-touch device, you can see that the
terminal prints the subset of the data structures that refers only to
the contact data. This is because the query ..contacts
addresses to any data named as contacts
, searched at any level
of the structure. Such query language can be very useful during
development and testing phases for highlighting only the relevant
information.
A more exhaustive description of the data structure and of the query
language can be found in the data model section. For
now, let’s leave the data structure and we consider the functioning of
the pipeline: it’s not difficult to understand that the |
operator (Pipe) is used to connect in parallel the nodes viz:
and
dump:
, so that the products are sent to both of
them. Figure 3.2 shows the structure of the current
pipeline.
Figure 3.2: Pipeline obtained from the configuration
in.tuio://:3333 + (viz: | dump:)
.
A key feature of Boing is the ability to provide the captured input events to external applications. This enables in most of the cases to take advantage of the toolkit’s features without the need to adapt or to modify the applications, while sometimes a simple configuration may be required. As shown in figure 3.3, the Boing toolkit works as a semi-transparent layer placed between the input sources and the final applications.
Figure 3.3: Boing works as a semi-transparent layer placed in between the devices and the applications for processing and transmitting the input events.
Thanks to the many supported encodings, Boing can easily fit different combinations of devices and applications. In this basic example, let’s consider to have an application listening for a TUIO stream on the local port 3335 [2]. If you don’t have a TUIO application, simply open a new terminal and launch a new Boing instance using the command:
boing "in.tuio://:3335 + viz:"
In the previous example you connected one input device to two output
nodes. The |
operator also enables to put in parallel
different inputs, like for example a second multi-touch device enabled
to send its TUIO messages to the local port 3334. Let’s try a new
pipeline by running the command:
boing "(in.tuio://:3333 | in.tuio://:3334) + (viz: | out.tuio://127.0.0.1:3335)"
Figure 3.4 shows the structure of the new pipeline.
Figure 3.4: Pipeline obtained from the configuration
(in.tuio://:3333 | in.tuio://:3334) + (viz: | out.tuio://127.0.0.1:3335)
.As you can see, a very important feature of Boing is that you can simultaneously connect many devices to different applications. Such feature eases the usage of debugging tools and it enables multi-device and multi-user applications.
The Boing toolkit is not only able to redirect input data to different destinations, but it also enables to process the transferred data. With regard to the multi-touch devices, recurring operations are the removal of the sensor noise and the calibration of the touch points. In order to accomplish these tasks, the toolkit provides two functional nodes that can be easily employed in our pipelines. As an example, let’s run a new pipeline using the following command:
boing "in.tuio://:3333 + filtering: + calib:?screen=left + viz:"
Now, when you touch your tactile device you should still see the
interactions on the visualizer widget, but now they look more smooth
and they are rotated 90 degrees counterclockwise. By employing the
filtering:
node, we added the default smoothing filter, which
is applied by default to the position of the contact points, while the
node calib:
performs the calibration of the touch points.
The structure of the current pipeline is shown in figure 3.5.
Figure 3.5: Pipeline obtained from the configuration
in.tuio://:3333 + filtering: + calib:?screen=left + viz:
In order to better understand the result of the processing stage, it may be useful to show at the same time the raw data and the processed one. In order to achieve such result, stop the previous pipeline and run the following command:
boing "in.tuio://:3333 + (filtering: + calib:?screen=left + edit:?source=filtered | nop:) + viz:"
Now, when you touch your input device you can see on the visualizer
widget both the raw tracks and the processed tracks, so that it is
easier to note the effect of the processing stage. The structure of
the modified pipeline is shown in figure 3.6. Note
that this behaviour has been obtained by adding a parallel branch
constituted only by the node nop:
, which simply forwards the
incoming data without making any modifications, and adding the node
edit:?source=filtered
, which labels the events of the
processing branch so that they belong to the source filtered (the
name is not relevant). This latter step is necessary since the data of
the two parallel branches is merged into a single stream before being
passed to the visualizer widget.
Figure 3.6: Pipeline obtained from the configuration
in.tuio://:3333 + (filtering: + calib:?screen=left +
edit:?source=filtered | nop:) + viz:
The Boing toolkit also provides some tools for recording input
events into log files and some other tools for replaying them. These
operations are often really helpful during the development and
debugging of applications. The simplest way to log events into a file
is to use the node log:
. As an example, consider running the
following command:
boing "in.tuio://:3333 + (viz: | log:./log.bz2)"
Now, all the gestures you make on your tactile device will be recorded
and written to the file ./log.bz2
. Then, stop the pipeline by
pressing Ctrl-C and let’s replay the recorded gestures by executing
the command:
boing "play:./log.bz2 + viz:"
Quite easy, isn’t it? It is also possible to configure the player to endlessly rerun the log and set the replay speed. To do so, simply run this command:
boing "play:./log.bz2?loop&speed=0.2 + viz:"
A more powerful tool for replaying log files is the player:
node: thanks to its GUI, it enables users to easily define a playlist
of log files that the node will reproduce. As an example, run the
following command:
boing "player: + viz:"
Playlists can be exported so that the player:
tool becomes
very useful during the application testing for executing the unit
test.
Footnotes
[1] | If you are unfamiliar with the TUIO protocol, consider having a look to the available TUIO trackers, or jumping to the Nodes reference table, in order to discover the different ways Boing exploits to connect to the input devices. |
[2] | For more output sources, see the Nodes reference table. |
Sometimes it can be useful to store the configuration of the pipeline
for later reuse. Writing long configurations in a terminal may also be
quite annoying. For these reasons, Boing lets users to write the
configuration of the pipeline in a text file and then to load such
configuration using the special node conf:
.
As an example, consider you have finally wrote the configuration of a
pipeline for comparing the result of different smoothing filters. Now
you want to save it in a file (e.g. config-filters.txt
) and maybe
you want to add some comments that will help you understanding the
structure of the pipeline. The file may look like the following:
# Pipeline for comparing the result of different smoothing filters.
#
# Author: Me
# Date: Sept 12, 2012
# File: config-filters.txt
# ONE INPUT: a standard TUIO source
in.tuio://:3333
# PARALLEL BRANCHES
+ (
# Raw input
nop:
# Default filter
| filtering: + edit:?source=Default
# Moving mean filter
| filtering:/moving/average?winsize=5 + edit:?source=Mean
# Moving median filter
| filtering:/moving/median?winsize=5 + edit:?source=Median
# Exponential double filter
| filtering:/exponential/double?alpha=1&gamma=1 + edit:?source=Exponential
# OneEuro filter
| filtering:/oneeuro?freq=1 + edit:?source=OneEuro
# ---
)
# ONE OUTPUT: the visualizer
+ viz:
Now, in order to run the pipeline you just have to enter the command:
boing conf:./config-filters.txt
Quite easy, isn’t it?
The node conf:
is actually a composite node that contains the
pipeline defined in the configuration file. For this reason, it is
also possible to use the conf:
node into another
pipeline. Consider as an example that you have a multi-touch table
sending contact information via the TUIO protocol, you found a good
smoothing filter since the input is quite noisy and you also
determined the calibration matrix to fit the touch position to the
correct screen space. You are not going to change these parameters so
you would like to consider all these elements as an atomic input
source that does not mess up a larger configuration. Thus, first you
could write the configuration of your input source into a file
(e.g. my-mt-table.txt
), which may look like the following:
# My multi-touch table without its ugly noise and well calibrated.
#
# Author: Me
# Date: Sept 12, 2012
# File: my-mt-table.txt
# INPUT: standard TUIO source
in.tuio://:3333
# FILTERING: OneEuro filter
+ filtering:/oneeuro?freq=60&merge
# CALIBRATION: my 4x4 matrix
+ calib:?merge&matrix=0.98,0,0,0.021,0,0.83,0,0.010,0,0,1,0,0,0,0,1
# NO OUTPUT, so I can reuse this into an external pipeline.
Then, you can reuse your configured input device as an atomic item in
a new pipeline. As an example, let’s show the contact events using the
viz:
node, and at the same time use the recorder widget and
forward the contacts to an other application listening for a TUIO
source on the local port 3334. The command to run is the following:
boing "conf:./my-mt-table.txt + (viz: | rec: | out.tuio://127.0.0.1:3334)"
As you can see, saving pipeline configurations into files can be quite
useful in different situations. Needless to say that you can also use
the conf:
node inside a configuration written in a file, so that
it is possible to arrange items in a hierarchical structure.
Developers can easily deploy pipelines by invoking the Boing’s
API in their Python code. The function boing.create()
can be used to instantiate the pipeline’s nodes. This method requires
as argument an URI expression that is used to specify the
functionality of the nodes to be created and how they are
connected. The Nodes reference table is the same as for the command line
script. Then, the operators +
and |
can be used
compose the pipeline.
The following code can be used as an example for creating Boing pipelines:
#!/usr/bin/env python3
import sys
import PyQt4
import boing
# Init application
app = PyQt4.QtGui.QApplication(sys.argv)
# Create nodes
n1 = boing.create("in.tuio://:3333")
n2 = boing.create("viz:")
n3 = boing.create("dump:?request=$..contacts")
# Compose the pipeline
graph = n1 + (n2 | n3)
# Run
sys.exit(app.exec_())
Todo
Describe an example of functional node.
The documentation is structured into the following sections:
Boing pipelines are made by directed graphs, where the edge direction defines the data flow between the nodes. There are three types of nodes:
The type of a node directly influences how the node can be connected to the other nodes: producers only accept outgoing connections, while consumers accept incoming connections only. Workers are composed by both the producer and consumer interfaces, so they can have both incoming and outgoing connections. Figure 4.1 shows an example of both valid and invalid connections.
The core infrastructure of Boing pipelines is the producer-consumer model, which defines how the data is propagated through the pipeline. The model performs a pull technology, but it is extended by using the Observer pattern: consumers must subscribe to the producers in order to receive their products; for each subscribed consumer, producers keep a record containing the list of the pending products. When a producer has a new product, for each registered consumer it enqueues the product in the associated product list and it triggers the consumer, which synchronously or asynchronously can require its own pending products. Then, at the consumer’s request, the producer sends all the correspondent pending products to the consumer and it cleans the correspondent buffer. The entire pipeline is run in a single thread, thus an eventloop is used to handle the asynchronous nodes. Figure 4.2 shows the UML sequence diagram that defines the data exchange between producers and consumers.
See also
classes boing.core.Producer
and
boing.core.Consumer
In many situations, a data source can provide a wide range of information, but consumers may not be interested in all of it. For this reason, in order to save processing time, the model permits to assign a request to each consumer. Every time a producer has a new product, it tests the request of each registered consumer and only if it matches the product, the producer notifies the consumer the new product. This behavior enables to process and transfer only the useful information, while the useless part is not processed. Requests can be added up so that a producer can easily know the entire request of all its registered consumers. The union of all the registered consumers’ requests is called aggregate demand.
On the other side, it is good to know what a producer can supply. For this reason the model permits to assign an offer to the producers, which must be the list of templates of the products it can provide. Using its offer, a producer can say a priori whether it can meet a consumer’s request. Composing the offer and the aggregate demand, it is possible to calculate the demanded offer, which represents the subset of the offer that is currently being demanded.
As an example, consider two producers P1 and P2 and two consumers C1 and C2 connected as shown in figure 4.3. It is possible to observe that the aggregate demand of P1 is equal to the union of the requests of both C1 and C2. Moreover, even if P1 produces both A and B, only the products A are sent to C1, while both A and B products are sent to C2. Also note that P2‘s demandedOffer is only B, because P2 is only connected to C2 and this one does not require the products C.
Note
It is important to understand that a node’s offer does not impose that the only products that the nodes produces are coherent with the offer and even that it is sure that the node will ever produce such products. The offer is only used to describe the node standard behavior. It’s easier said than done!
See also
classes boing.core.Offer
and boing.core.Request
As previously seen, it is possible to create long pipelines by serializing worker nodes. In order to spread the supply and demand strategy, a worker node must be able to propagate the requests of the consumers it is connected to in addition to its own request and to propagate the offer of the producers it is connected to in addition to its own offer. In order to understand such necessity, consider the pipeline shown in figure 4.4: in this case the worker W is not propagating its neighbors’ requests and offers (the variables isPropagantingRequest and isPropagatingOffer are false), so that its own request and offer, which are defined by the variables _selfRequest and _selfOffer, are actually the same of its (public) request and offer. In this case, it is possible to notice that even if the consumer C require the products B, such demand is hidden by the worker W, so that even if the producer P can provide B products, it can’t see anyone interested to them, so they are not produced.
Figure 4.4: The worker W is not propagating its connected consumers’ requests, thus the producer P does not provides the products B.
The figure 4.5 shows the same pipeline as before with the difference that the worker W is now propagating its neighbors’ requests and offers. It is possible to notice that the request of W is equal to the union of the request of C and its own request, and its public offer is equal to the union of the offer of P and its own offer. W is now requiring B products because a subsequent node is also requiring them, thus P will produce and dispatch them.
Note
It is important to understand that the variables
isPropagatingRequest and isPropagatingOffer do not control the
output of W, but only the fact that its request and offer are
determined by accumulating the neighbors requests and offers. The
fact that W forwards B products only depends on the specific
implementation of W. See class boing.core.Functor
for
product forwarding cases.
As formerly described, worker nodes are both consumers and producers, and they can be considered as the pipeline’s processing units. Workers normally calculate simple or atomic operations because they can be easily serialized in order to compose more complex processing pipelines. Boing pipelines can be modified dynamically in order to evolve and fit a flexible environment. This may entail that not all the processing units are really necessary in order to compute the expected result. In order to avoid a waste of time, the pipeline exploits a auto-configuration technique based on the nodes’ supply-demand knowledge. This technique, exploited by the Wise Workers, can be summarized into the following two rules:
As an example consider the pipeline in figure 4.6: the producer P provides the products A, which are required by the consumer C; this one also requires the products B, but P cannot provide them. For this reason the worker W, which can produce B from A, has been employed. Since B is required by C, W is currently active. In this example the worker W is set to forward all the products it receives even it is not directly interested to them.
Figure 4.6: The producer P provides the products A, while the worker W produces the products B using the products A. Both A and B are actually required by the consumer C.
Now suppose that the consumer C changes its own request to A only. In this case, nobody is interested to B anymore, thus, following the first rule of the Wise Worker, the worker stops requiring A for itself and it passes into an inactive state, but, since it is propagating C‘s requests, it still requires A products. Figure 4.7 shows the state of the pipeline in this case.
Figure 4.7: If C does not require products B anymore, the worker W automatically stops producing them and requiring A products for itself, but since it is propagating C‘s requests, it still requires A products so it can forward them to C.
Considering the pipeline in figure 4.6, a different situation may arrive: if the producer P changes its offer to D, no one will provide the products A, thus, following the second rule of the Wise Worker, since the worker’s request is not satisfied anymore, it nullifies its own offer. The resulted pipeline is shown in figure 4.8. In this case requests do not change, so that no more products are exchanged between the nodes.
Figure 4.8: Considering the pipeline of figure 4.6, if the producer P starts producing B only, the worker’s request is not satisfied anymore, so it automatically nullifies its default offer.
In some cases workers do not previously know the products they provide since it only depends on the products they will receive. As an example, a worker may forward only a subset of the products it receives or it may make simple changes to the products it requires and then forward them. In those cases, it is not possible to set the offer in advance of the pipeline execution, thus the first rule of the Wise Worker cannot be applied. In order to handle those cases, the Wise Workers can use the Tunneling exception, that makes the first rule considering the entire propagated offer instead of the worker’s own offer.
As an example consider the pipeline in figure 4.9: the worker W simply forwards the products it receives so it has not its own offer. Despite this, thanks to the tunneling exception, W is still active, since its global offer matches the request of C.
Figure 4.9: When using the tunneling option, the propagated offer is considered to determine if the worker is active instead of its own offer only.
Concrete workers using the tunneling feature are the
Filter
and
Calibration
classes.
See also
classes boing.core.WiseWorker
and
boing.core.Functor
Todo
Node URIs | ||||
---|---|---|---|---|
OSs | Mode | Value | Query keys [1] | Description |
Data Redirection | ||||
LWX | I | in[.<encoding>]<InputDevice> | listen and decode data from an input device | |
LWX | O | out[.<encoding>]<OutputDevice> | encode and forward the data to a target destination | |
Record/Replay | ||||
LWX | I | play[.<encoding>]:<filepath> | loop, speed, interval | replay a log file (default encoding pickle) |
LWX | O | log[.<encoding>]:<filepath> | record data to log file (default encoding pickle) | |
LWX | O | rec: | request, timelimit, sizelimit, oversizecut, fps, timewarping | data recorder with GUI |
LWX | I | player[.<encoding>]: | interval, open | log files player with GUI (default encoding pickle) |
Data Debug | ||||
LWX | O | dump[.<encoding>]<OutputDevice> | request, mode, separator, src, dest, depth | dump products to an output device |
LWX | O | stat[.<encoding>]<OutputDevice> | request, fps | print products statistics to an output device |
LWX | O | viz: | antialiasing, fps | display multi-touch contacts |
Data Processing | ||||
LWX | W | nop: | no operation node | |
LWX | W | edit: | merge, copy, result, **dict | apply to all the received products **dict |
LWX | W | calib: | matrix, screen, attr, request, merge, copy, result | apply a 4x4 transformation matrix |
LWX | W | filtering:[<filter-path>] | attr, request, merge, copy, result, <filter-attr> [2] | filter product data |
LWX | W | timekeeper: | merge, copy, result | mark each received product with a timetag |
LWX | W | lag:[<msec>] | add a lag to each received product | |
Utils | ||||
LWX | [3] | conf:<filepath> | composite node containing the pipeline defined into the specified configuration file. |
Encodings [4] | ||||
---|---|---|---|---|
OSs | Mode | Value | Query keys | Description |
LWX | IO | slip | bytestream from/to SLIP | |
LWX | I | pickle | noslip | pickle to products |
LWX | O | pickle | protocol, request, noslip | Products to pickle |
LWX | I | json | noslip | JSON to products |
LWX | O | json | request, noslip | products to JSON |
LWX | IO | osc | rt, noslip | bytestream from/to OSC |
LWX | IO | tuio[.osc] | rawsource | Multi-touch events from/to TUIO |
Input/Output devices | ||||
---|---|---|---|---|
OSs | Mode | Value | Query keys | Description |
LX | I | :[stdin] | read from standard input | |
LWX | I | :[stdout] | write to standard output | |
LWX | I | [.file]:<filepath> | uncompress, postend | read from file |
LWX | O | [.file]:<filepath> | write to file | |
LWX | I | [.udp]://<host>:<port> | read from UDP socket | |
LWX | O | [.udp]://<host>:<port> | writeend | write to UDP socket |
LWX | IO | .tcp://<host>:<port> | writeend | read/write on TCP socket |
Hosts | |||
---|---|---|---|
OSs | Mode | Value | Description |
LWX | I | empty | same as IPv4 any address |
LWX | I | 0.0.0.0 | IPv4 any address |
LWX | I | [::] | IPv6 any address |
LWX | IO | 127.0.0.1 | IPv4 loopback |
LWX | IO | [::1] | IPv6 loopback |
LWX | IO | x.x.x.x | specific IPv4 address |
LWX | IO | [x:x:x:x:x:x:x:x] | specific IPv6 address |
LWX | IO | <hostname> | specific hostname |
OS support | |
---|---|
Value | Description |
L | Linux |
W | Windows 7 [5] |
X | OS X |
Footnotes
[1] | The available query keys are obtained from the union of the
available query keys of all the uri components. As an
example, the URI out.json://[::1]:7777 is by default
translated to out.json.udp://[::1]:7777 , so it owns the
query keys of the JSON encoder (request and filter ) and of
the udp socket node (writeend ). |
[2] | <filter-attr> dependes on the requested filter. |
[3] | The mode depends on the pipeline defined into the configuration file. It is important to note that pipelines may have a closed configuration, which means they do not behave neither as input nor output, nor worker. This happens when all the inputs are connected in series to the outputs. |
[4] | Some encodings have default input/output devices
(e.g. in.tuio: is by default translated into
in.tuio.udp://[::]:3333 ). |
[5] | On Windows, in order to define a file using the scheme
file: it is necessary to place the character ‘/’ (slash)
before the drive letter
(e.g. file:///C:/Windows/explorer.exe ). |
This section presents the different modules that constitute the toolkit Boing. The documentation of the classes and functions of the toolkit’s API is hereinafter provided.
Users API:
boing
— Creating and managing pipelines¶Developers can easily deploy Boing pipelines by invoking the toolkit’s
API in their Python code. The most important element is the function
boing.create()
, which is used to instantiate the nodes of
the pipeline.
boing.
create
(expr, parent=None)¶Return a new node created as defined in the expression expr, with parent object parent. If expr is composed by a single URI, the returned object will be a new node correspondent to the provided URI; if expr is formed by an URI expression, the returned object will be a composed node.
All the available nodes are listed and described in the Nodes reference table.
In order to compose the pipeline, the nodes can be attached using the
Python operators +
and |
, which work the same as the
operators used in the URI expressions. As an example, consider the
following code:
n1 = boing.create("in.tuio:")
n2 = boing.create("viz:")
n3 = boing.create("dump:")
pipeline = n1 + (n2 | n3)
The same pipeline can be obtained using the following code:
pipeline = boing.create("in.tuio:+(viz:|dump:)")
In order to run the pipeline it is necessary to launch the Qt Application that should have been initialized before creating the pipeline. The following code can be used as an example for creating custom Boing pipelines:
#!/usr/bin/env python3
import sys
import PyQt4
import boing
# Init application
app = PyQt4.QtGui.QApplication(sys.argv)
# Create nodes
n1 = boing.create("in.tuio:")
n2 = boing.create("viz:")
n3 = boing.create("dump:?request=$..contacts")
# Compose the pipeline
graph = n1 + (n2 | n3)
# Run
sys.exit(app.exec_())
Any global configuration variable can be stored in the attribute
boing.config
.
boing.
config
¶dict
object used to store any global configuration
variable. Boing‘s own variables:
"--no-gui"
: defines whether the GUI widgets are enabled. If
set to True
, nodes likes viz:
, rec:
, player:
,
etc., cannot be created."--no-raise"
: defines whether the GUI widgets should be
automatically raised as they are initialized (see method
QtGui.QWidget.raise_()
).Todo
Describe how to configure the pipeline dinamically
boing.
activateConsole
(url="", locals=None, banner=None)¶Enable a Python interpreter at url.
The optional locals argument specifies the dictionary in which code will be executed; it defaults to a newly created dictionary with key “__name__” set to “__console__” and key “__doc__” set to None.
The optional banner argument specifies the banner to print before the first interaction; by default it prints a banner similar to the one printed by the real Python interpreter.
The pipeline architecture:
boing.core
— The pipeline infrastructure¶The module boing.core
contains all the classes that constitute
the infrastructure of Boing pipelines.
The Producer
and Consumer
classes are build over the
Observer design pattern of the module boing.core.observer
: the
Producer is an Observable object enabled to post products; a consumer
is an Observer object that can subscribe itself to many
producers. When a producer has a new product, it triggers the
registered consumers; the triggered consumers will immediately or at
regular time interval demand the producer the new products.
A Worker
object is both a Producer
and a
Consumer
and it is used as base class for defining processing
nodes. By connecting producers, workers and consumers it is possible
to create processing pipelines.
A WiseWorker
is a Worker
able to automatically
detect whenever it should not propose its own offer or its own
request; this is done in order to save computational time.
The Functor
class is practical base class for inheriting
custom processing Worker
objects. Instead of implementing the
classic Consumer._consume()
method, the Functor
proposes the more powerfull method Functor._process()
.
Multiple Producer
, Consumer
and Worker
instances can be composed into Composite
objects. There are
three types of composites:
CompositeProducer
works as it was a single producer;CompositeConsumer
works as it was a single consumer;CompositeWorker
works as it was a single worker;See also
boing.core.
Producer
(offer, tags=None, store=None, retrieve=None, haspending=None, parent=None)¶Producer
instances are Observable
objects able to post products to
a set of subscribed Consumer
instances. The argument
offer must be an instance of Offer
and it define the
products this producer will supply, while tags must be a dict or
None. The argument store can be a callable object to be used as a
handler for storing posted products (see _store()
for the
handler arguments) or None, while retrieve can be a callable
object to be used as a handler for retrieving stored products (see
_retrieveAndDeliver()
for the handler arguments) or
None. parent defines the consumer’s parent.
When a producer is demanded to posts a product, for each registered consumer it tests the product with the consumer’s request and only if the match is valid it triggers the consumer.
Public signals:
demandChanged
¶Signal emitted when the aggregate demand changes.
offerChanged
¶Signal emitted when its own offer changes.
demandedOfferChanged
¶Signal emitted when its own demanded offer changes.
Available methods:
aggregateDemand
()¶Return the union of all the subscribed consumers’ requests.
demandedOffer
()¶Return the producer’s demanded offer.
meetsRequest
()¶Return whether the product’s offer meets request.
offer
()¶Return the producer’s offer.
postProduct
(product)¶Post product. In concrete terms, it triggers the registered consumers that require product, then it stores the product.
boing.core.
Offer
(*args, iter=None)¶An Offer
defines the list of products that a producer
advertises to be its deliverable objects.
Note
A producer’s offer only estimates the products that are normally produced. There is no guarantee that such products will ever be posted, neither that products that do not match the offer won’t be produced.
Offer.UNDEFINED
can be used to define the producer’s
offer, when the real offer cannot be defined a priori. This avoids
to have empty offers, when they cannot be predeterminated.
boing.core.
Consumer
(request, consume=None, hz=None, parent=None)¶Consumer
objects are Observer
objects that can be subscribed to
several Producer
instances for receiving their
products. When a producer posts a product, it triggers the
registered consumers; then the consumers will immediately or at
regular time interval demand to the producer the new products.
Warning
Many consumers can be subscribed to a single producer. Each new product is actually shared within the different consumers, therefore a consumer MUST NOT modify any received product, unless it is supposed to be the only consumer.
Consumers have a Request
. When a producer is demanded to
posts a product, it tests the product with the consumer’s request
and only if the match is valid it triggers the consumer.
request
()¶Return the consumer’s request.
_consume
(products, producer)¶Consume the products posted from producer.
boing.core.
Request
¶The class Request
is an abstract class used by
Consumer
objects for specifing the set of products they
are insterested to. The method test()
is used to check
whether a product matches the request.
Request.NONE
and Request.ANY
define respectively
a “no product” and “any product” requests.
Request
objects may also indicate the
internal parts of a product to which a producer may be
interested. The method items()
returns the sequence of the
product’s parts a producer is interested to.
The class Request
implements the
design pattern “Composite”: different requests can be combined into
a single request by using the sum operation (e.g. comp =
r1 + r2
). A composite request matches the union of the products
that are matched by the requests whom it is
composed. Request.NONE
is the identity element of the sum
operation.
Request
objects are immutable.
test
(product)¶Return whether the product matches the request.
items
(product)¶Return an iterator over the product‘s internal parts (i.e. (key, value) pairs) that match the request.
boing.core.
Worker
¶A Worker
instance is both a Producer
and a
Consumer
. The class Worker
is by itself an
abstract class. Consider using the concrete class
BaseWorker
instead.
boing.core.
BaseWorker
(request, offer, tags=None, store=None, retrieve=None, haspending=None, consume=None, hz=None, parent=None)¶A BaseWorker
is the simplest concrete Worker
. By
default it does nothing with the products it receives, but it is
able to propagate the offers of the producers it is subscribed to,
and it is able to propagate the requests of the consumers that are
subscribed to it.
boing.core.
NopWorker
(store=None, retrieve=None, hz=None, parent=None)¶NopWorker
instances simply forwards all the products they
receive.
boing.core.
WiseWorker
(request, offer, **kwargs)¶A WiseWorker
instance is able to automatically detect
whenever it should not propose its own offer or its own request;
this is done in order to save computational time. The behaviour of
WiseWorker
objects works as follows:
A WiseWorker
is active (see method isActive()
) when
its own request is not deactivated (i.e. case 1).
A WiseWorker
can be forced to be activated or
deactivated. Use the method setForcing()
to impose a specific
behaviour.
Use WiseWorker.TUNNELING
as the worker Offer, when the
worker is supposed to process and then forward a subset of the
products it receives. This is necessary since sometimes it may be
possible that the worker cannot have a predefined offer since it
always depend from the offer of the observed producers.
isActive
()¶The WiseWorker
is active only if its offer is requested
from any subscribed consumer, unless it is forced (see
forcing()
).
isTunneling
()¶Return True if the worker is forwarding a subset of the products it receives.
forcing
()¶Return whether the WiseWorker is being forced. Return a value
within (WiseWorker.ACTIVATED
,
WiseWorker.DEACTIVATED
, WiseWorker.NONE
).
setForcing
(forcing)¶Impose to the WiseWorker
to be activated, deactivated
or remove a previous forcing. forcing must be a value within
(WiseWorker.ACTIVATED
, WiseWorker.DEACTIVATED
,
WiseWorker.NONE
).
ACTIVATED
¶Value used to force the WiseWorker
to be activated.
DEACTIVATED
¶Value used to force the WiseWorker
to be deactivated.
NONE
¶Value used to let the WiseWorker
decide it it should be
active.
TUNNELING
¶Value used to set the WiseWorker
the tunneling mode.
boing.core.
Functor
(args, offer, blender, process=None, **kwargs)¶The Functor
class is practical base class for inheriting
custom processing Workers
. Instead of implementing the
classic Consumer._consume()
method, the Functor
proposes the more powerfull method _process()
. This handler
method receives as argument sequence, an iterator over the
operands, which are iterators over the couples (key, value)
obtained from applying the method Request.items()
of the
request of the functor on the list of received products. This
enables to access directly to the name and values of the required
data without the need of reimplementing the code to get them. The
method _process()
is a generator method and it it supposed to
yield the the couples (key, value) representing the result of the
node processing. The yielded results are automatically considered
by the functor to create a new product that will be automatically
posted.
The functor uses a Functor.Blender
object to create the
new product. A set of predefined blenders are used to set the
functor behaviour:
Functor.MERGE
— Join the original product and results
of the functor.Functor.MERGECOPY
— Make a deep copy of the original
product and then join the results of the functor.Functor.RESULTONLY
— Post as product only the result of
the functor.A Functor
instance propagates the requests only if the
current blender is a MergeBlender
and it propagates the
offers only if the current blender is a MergeBlender
or if
it is tunneling (see WiseWorker.isTunneling()
).
blender
()¶Return the current active Functor.Blender
.
_process
(sequence, producer)¶This handler method receives as argument sequence, an iterator
over the operands, which are iterators over the couples (key,
value) obtained from applying the method Request.items()
of the request of the functor on the list of received
products. This enables to access directly to the name and values
of the required data without the need of reimplementing the code
to get them. This is a generator method and it it supposed to
yield the the couples (key, value) representing the result of
the node processing.
boing.core.
Composite
(*internals, parent=None)¶A Composite
instance stores a set of internal objects
using strong references. Use the method internals()
to scroll
the references objects.
internals
()¶Return an iterator over the set of internal nodes for which a strong reference is stored.
boing.core.
CompositeProducer
(*producers, internals=set(), parent=None)¶A CompositeProducer
combines in parallel the list of
producers so that they can be considered as a single
Producer
. The argument internals can be used to specify
the object for which a strong reference should be kept. All
producers are by default added as internals of the
Composite
.
producers
()¶Return an iterator over the first level producers.
boing.core.
CompositeConsumer
(*consumers, internals=set(), parent=None)¶A CompositeConsumer
combines in parallel the list of
consumers so that they can be considered as a single
Consumer
. The argument internals can be used to specify
the object for which a strong reference should be kept. All
consumers are by default added as internals of the
Composite
.
consumers
()¶Return an iterator over the first level consumers.
boing.core.
CompositeWorker
(consumers, producers, internals=set(), parent=None)¶A CompositeWorker
object combines in parallel the list of
producers and in parallel the list of consumers so that they
can be considered as a single Worker
. The argument
internals can be used to specify the object for which a strong
reference should be kept. All producers and consumers are by
default added as internals of the Composite
.
boing.core.observer
— Observables and observers¶The module boing.core.observer
provides an implementation of
the Observer design pattern.
Rather than the standard behaviour, this implementation enables the
Observable
objects to trigger only a subset of all the
current registered Observer
objects.
Note
The notification mechanism relies on the SIGNAL-SLOT mechanism of the Qt eventloop, thus a QApplication must be running in order to ensure that notifications are processed.
boing.core.observer.
Observable
(parent=None)¶Observable
objects can be subscribed by a list of
Observer
instances. An Observable can trigger all or only
a subset of the subscribed observers by invoking the method
trigger()
. The attribute parent defines the parent object
of the observable.
observerAdded
¶Signal emitted when a new observer is added.
observerRemoved
¶Signal emitted when a registered observer is removed.
observers
()¶Return an iterator over the subscribed observers.
addObserver
(observer, mode=QtCore.Qt.QueuedConnection)¶Subscribe observer as a new observer. Return whether observer has been correctly added.
removeObserver
(observer)¶Unsubscribe observer. Return whether observer has been correctly removed.
clear
()¶Unsubscribe all registered observers.
notify
(*restrictions)¶Trigger all the subscribed observers if restrictions is empty, otherwise trigger only the registered observers in restrictions.
Note
The list of the subscribed observers is composed by weak references, so it is necessary to keep both observables and observers alive.
boing.core.observer.
Observer
(react=None, hz=None, parent=None)¶Observer
objects can be subscribed to many
Observable
instances in order to listen to their
notifications. The argument react can be set to the handler
function (it must accept one argument) that will be called as
consequence of an observable notification. If react is None, the
member method _react()
will be called. hz defines the rate
at which the observer will react to notifications. Available
values are:
parent defines the observers parent.
observableAdded
¶Signal emitted when the observer is subscribed to a new observable.
observableRemoved
¶Signal emitted when the observer is unsubscribed from an observable.
observed
()¶Return an iterator over the observables it is subscribed to.
subscribeTo
(observable, mode=QtCore.Qt.QueuedConnection)¶Subscribe to observable. Return whether observer has been successfully subscribed to.
unsubscribeFrom
(observable)¶Unsubscribe from observable. Return whether observable has been successfully found and removed.
clear
()¶Unsubscribe from all observed observables.
hz
()¶Return when the observer will react to the notifications. Possible values:
queue
()¶Return an iterator over the observables that have triggered without having being reacted to yet.
_react
(observable)¶Handler method invoked as a result of the notification of
observable, but only if the Observer
instance has
not been created with a custom react handler.
Note
The list of the subscribed observables is composed by weak references, so it is necessary to keep both observables and observers alive.
Preconfigured nodes:
boing.nodes
— The nodes of the pipeline¶The module boing.nodes
contains a set of generic utility nodes.
boing.nodes.
DataReader
(inputdevice, postend=True, parent=None)¶Producer
node that anytime the
device inputdevice send the signal readyRead
it reads the
device and it produces a message containing the data. The provided
products is a dictionary {"str": data}
if data
is a string,
otherwise the product will be a dictionary like {"data": data}
.
If the argument postend is set to True
, the
DataReader
will never produce an empty product, like
{"str": ""}
or {"data": b""}
. parent defines the parent
of the node.
inputDevice
()¶Return the considered input device.
boing.nodes.
DataWriter
(outputdevice, writeend=True, hz=None, parent=None)¶Consumer
node that anytime it
receives some data, it writes the data to the device outputdevice. The
DataWriter
requires the products str
if the output
device is text enabled (see method isTextModeEnabled
) otherwise it
requires the product data
. If the argument writeend is set to
True
, the DataWriter
will never write an empty string;
this can be useful in order to prevent a socket to close. parent
defines the parent of the node.
outputDevice
()¶Return the considered output device.
boing.nodes.
Dump
(request=Request.ANY, mode='items', separator='\n\n', src=False, dest=False, depth=None, parent=None)¶Instances of the Dump
class produce a string
representation of the products they receive. The string is
obtained using the function boing.utils.deepDump()
.
The parameter request must be an instance of the class
boing.core.Request
and it is used to select the product
to be dumped. The default value for request is
Request.ALL
. mode defines how the received
products will be dumped. The available values are:
'keys'
, only the matched keys are written;'values'
, only the values of the matched keys are written;'items'
, both the keys and values are written.separator defines the string to be written between two
products. The default value for separator is '\n\n'
. src
defines whether the node also dumps the producer of the received
products. The default for src is False. The paramenter dest
defines whether the node adds a reference to itself when it dumps
the received products; its default value is False. The parameter
depth defines how many levels of the data hierarchy are explored
and it is directly passed to the boing.utils.deepDump()
function.
mode
()¶Return the node’s mode.
setMode
(mode)¶Set the node’s dump mode.
boing.nodes.
Editor
(dict, blender, parent=None)¶Instances of the Editor
class apply to the received
products the (key, values) pairs of dict.
blender defines the output of the node (see
boing.core.Functor
). parent must be a
PyQt4.QtCore.QObject
and it defines the node’s parent.
get
(key, default=None)¶Return the value for key if key is in the editor’s dictionary, else default. If default is not given, it defaults to None.
set
(key, value)¶Set the value for key to value.
items
()¶Return a new view of the editor dictionary’s items ((key, value) pairs).
boing.nodes.
DiffArgumentFunctor
(functorfactory, request, blender=Functor.MERGECOPY, parent=None)¶It takes a functorfactory and for each different argument path, it creates a new functor which is applied to the argument value. The args must be a diff-based path so that functor can be removed depending on ‘diff.removed’ instances.
boing.nodes.
Timekeeper
(blender=Functor.MERGECOPY, parent=None)¶Instances of the Timekeeper
class tag each received
product with the timestamp when the product is received; then they
forward the product.
blender defines the output of the node (see
boing.core.Functor
). parent must be a
PyQt4.QtCore.QObject
and it defines the node’s parent.
Filtering algorithms:
boing.filtering
- Filters and noise generators¶Smoothing filters and noise generators.
Todo
Comment module boing.filtering
.
Gesture recognition algorithms:
boing.gesture.rubine
— The Rubine’s recognition algorithm¶The module boing.gesture.rubine
provides an implementation of
the Rubine’s gesture recognition algorithm.
boing.gesture.utils
— Recognizers’ common utilities¶The boing.gesture.utils module contains common method used by different recognizers.
boing.gesture.utils.
boundingBox
(points)¶Return the tuple (minx, miny, maxx, maxy) defining the bounding box for points.
boing.gesture.utils.
updateBoundingBox
(bb1, bb2)¶Return the tuple (minx, miny, maxx, maxy) defining the bounding box containing the bounding boxes bb1 and bb2.
Networking utilities:
boing.net
— Networking and encoding tools¶The module boing.net
provides classes and methods to ease the
usage of sockets and networking encodings, like for example JSON, OSC,
SLIP, etc.
boing.net.
Encoder
¶Abstract base class for implementing the encoders of all the different encodings.
encode
(obj)¶@abc.abstractmethod
Return the result obtained from encoding obj.
reset
()¶@abc.abstractmethod
Reset the encoder.
boing.net.
Decoder
¶Abstract base class for implementing the decoders of all the different encodings.
The Decoder class implements the composite pattern. Many decoders can be put in sequence into a single composed decoder using the sum operator.
decode
(obj)¶@abc.abstractmethod
Return the list of objects obtained from decoding obj.
reset
()¶@abc.abstractmethod
Reset the decoder.
boing.net.bytes
— UNICODE encoding¶The boing.net.bytes
module implements the adapter design
pattern by providing the standard string encoding functionalities as
Encoder and Decoder objects.
boing.net.bytes.
encode
(string, encoding="utf-8", errors="strict")¶Return an encoded version of string as a bytes object. Default encoding is ‘utf-8’. errors may be given to set a different error handling scheme. The default for errors is ‘strict’, meaning that encoding errors raise a UnicodeError. Other possible values are ‘ignore’, ‘replace’, ‘xmlcharrefreplace’, ‘backslashreplace’
boing.net.bytes.
decode
(data, encoding="utf-8", errors="strict")¶Return a string decoded from the given bytes. Default encoding is ‘utf-8’. errors may be given to set a different error handling scheme. The default for errors is ‘strict’, meaning that encoding errors raise a UnicodeError. Other possible values are ‘ignore’, ‘replace’ and any other name registered via codecs.register_error().
boing.net.slip
— SLIP encoding¶The module boing.net.slip
provides methods and classes for
supporting the SLIP protocol encoding and decoding.
boing.net.slip.
encode
(data)¶Return a slip encoded version of data.
boing.net.slip.
decode
(data, previous=None)¶Return the list of bytearrays obtained from the slip decoding of data followed by the undecoded bytes. If previous is not None, data is appended to previous before decoding. A typical usage would be:
buffer = bytearray()
decoded, buffer = decode(data, buffer)
boing.net.json
— JSON encoding¶The module boing.net.json
provides methods and classes for
supporting JSON object serialization. It uses the python json standard
module, but it provides a default solution for serializing bytestrings
and datetime.datetime objects.
Encoder and Decoder classes provide a standard interface for the JSON encoding.
boing.net.json.
encode
(obj)¶Return a string containing the json serialization of obj.
boing.net.json.
decode
(string)¶Return the object obtained for decoding string using the JSON decoding.
boing.net.osc
— OSC encoding¶The module boing.net.osc
provides methods and classes for
handling OSC formatted messages.
boing.net.osc.
Packet
¶Abstract base container of OSC data.
encode
()¶@abc.abstractmethod
Return the encoded representation of this packet.
debug
(out, indent="")¶@abc.abstractmethod
Write to out a string representation of the OSC packet. The argument indent can be used to format the output.
boing.net.osc.
Message
(address, typetags="", *arguments)¶Packet
object representing an OSC Message. The argument
address must be a string begginning with the character /
(forward slash). The argument typetags must be a string composed
by sequence of characters corresponding exactly to the sequence of
OSC arguments in the given message. arguments is the list of
object contained in the OSC Message.
address
¶String beginning with the character /
(forward slash).
String composed by a sequence of characters corresponding exactly to the sequence of OSC arguments in the given message.
arguments
¶List of the arguments of the message.
boing.net.osc.
Bundle
(timetag, elements)¶Packet
object representing an OSC Bundle. The argument timetag
must be a datetime.datetime
instance or None
, while
elements should be the list of Packet
objects contained
in the bundle.
timetag
¶None
or a datetime.datetime
instance.
boing.net.osc.
decode
(data, source=None)¶Return the Packet
object decoded from the bytestring
data. The argument source can be specified to set the packet
source.
The classes Encoder
and Decoder
provide a standard
interface for the OSC encoding.
>>> import sys
>>> import boing.net.osc as osc
>>> source = osc.Message("/tuio/2Dcur", "ss", "source", "test")
>>> alive = osc.Message("/tuio/2Dcur", "ss", "alive", "1")
>>> bundle = osc.Bundle(None,
(source, alive,
osc.Message("/tuio/2Dcur", "si", "fseq", 1)))
>>> data = bundle.encode()
>>> print(data)
b'#bundle\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00 /tuio/2Dcur\x00,ss\x00source\x00\x00test\x00\x00\x00\x00\x00\x00\x00\x1c/tuio/2Dcur\x00,ss\x00alive\x00\x00\x001\x00\x00\x00\x00\x00\x00\x1c/tuio/2Dcur\x00,si\x00fseq\x00\x00\x00\x00\x00\x00\x00\x01'
>>> packet = osc.decode(data)
>>> print(packet)
<Bundle instance at 0x1b756d0 [@None, 3 element(s)]>
>>> packet.debug(sys.stdout)
Bundle IMMEDIATELY
| /tuio/2Dcur ss 'source' 'test'
| /tuio/2Dcur ss 'alive' '1'
| /tuio/2Dcur si 'fseq' 1
boing.net.pickle
— Python pickle encoding¶The module boing.net.pickle
provides methods and classes for
supporting Python object serialization. It uses the python pickle
standard module.
Encoder and Decoder classes provide a standard interface for the pickle encoding.
boing.net.pickle.
encode
(obj, protocol=None)¶Return the pickled representation of obj as a bytes object.
The optional protocol argument tells the pickler to use the given protocol; supported protocols are 0, 1, 2, 3. The default protocol is 3; a backward-incompatible protocol designed for Python 3.0.
Specifying a negative protocol version selects the highest protocol version supported. The higher the protocol used, the more recent the version of Python needed to read the pickle produced.
boing.net.pickle.
decode
(data)¶Read a pickled object hierarchy from the bytes object data and return the reconstituted object hierarchy specified therein.
The protocol version of the pickle is detected automatically, so no protocol argument is needed. Bytes past the pickled object’s representation are ignored.
boing.net.pickle.
Encoder
¶The Encoder is able to serialize Python objects into pickle bytestrings.
encode
(obj)¶Return the pickled representation of obj as a bytes object.
The optional protocol argument tells the pickler to use the given protocol; supported protocols are 0, 1, 2, 3. The default protocol is 3; a backward-incompatible protocol designed for Python 3.0.
Specifying a negative protocol version selects the highest protocol version supported. The higher the protocol used, the more recent the version of Python needed to read the pickle produced.
reset
()¶NOP method.
boing.net.pickle.
Decoder
¶The Decoder object is able to decode pickle bytestrings into the corrispetive objects hierarchy.
decode
(obj)¶Read a pickled object hierarchy from the bytes object data and return the reconstituted object hierarchy specified therein.
The protocol version of the pickle is detected automatically, so no protocol argument is needed. Bytes past the pickled object’s representation are ignored.
reset
()¶Reset the slip internal buffer.
boing.net.ip
— IP utilities¶The module boing.net.ip
provides few functions related to
IP addressing.
boing.net.ip.
resolve
(addr, port, family=0, type=0)¶Return a pair (addr, port) representing the IP address associated to the host host for the specified port, family and socket type.
boing.net.ip.
addrToString
(addr)¶Return a string representing the QHostAddress addr.
boing.net.tcp
— TCP utilities¶Todo
Improve docs for the module boing.net.tcp
boing.net.udp
— UDP utilities¶Todo
Improve docs for the module boing.net.udp
boing.net.ntp
— NTP utilities¶The module boing.net.ntp
provides few functions for handling
the Network Time Protocol (NTP).
boing.net.ntp.
ntpEncode
(t)¶Return the bytes object obtained from encoding the POSIX timestamp t.
boing.net.ntp.
ntpDecode
(data)¶Return the POSIX timestamp obtained from decoding the bytes object data.
boing.net.ntp.
ntpFromServer
(server)¶Send an ntp time query to the server and return the obtained POSIX timestamp. The request is sent by using an UDP connection to the port 123 of the NTP server.
boing.net.ntp.
ntp2datetime
(t)¶Return the datetime.datetime
instance corresponding to
the POSIX timestamp t.
boing.net.ntp.
datetime2ntp
(dt)¶Return the POSIX timestamp corresponding to the
datetime.datetime
instance dt.
Usage example:
>>> import datetime
>>> import boing.net.ntp as ntp
>>> srvtime = ntp.ntpFromServer("europe.pool.ntp.org")
>>> srvdatetime = ntp.ntp2datetime(srvtime)
>>> now = datetime.datetime.now()
>>> print("Server time:", srvdatetime)
Server time: 2012-10-16 16:39:12.332479
>>> print("Local time:", now)
Local time: 2012-10-16 16:40:23.772048
>>> print("Delta:", now - srvdatetime)
Delta: 0:01:11.439569
Generic utilitities:
boing.utils
— Common utilities¶The module boing.utils
contains generic utility classes and
functions.
boing.utils.
assertIsInstance
(obj, *valid)¶Raise TypeError if obj is not an instance of a class in valid.
boing.utils.
deepDump
(obj, fd=sys.stdout, maxdepth=None, indent=2, end="n", sort=True)¶Write to fd a textual representation of obj.
boing.utils.
StateMachine
(initial=None)¶The StateMachine
class defines an object that owns a state
defined by a collections.Mapping
type object. The argument
initial can be used to define the initial state.
state
()¶Return the current state.
setState
(update=None, add=None, remove=None)¶Change the current state by applying update, add and remove.
applyDiff
(diff, feedback=False)¶Apply the provided diff to the current state. diff must
be a collections.Mapping
type containing any of the following keys:
'add'
: items that will be added to the current state;'update'
: items that will be update or added to the current state;'remove'
: items that will be removed from the current state.If feedback is True
the diff structure between the
previous state and the current state is provided as result of
the method.
boing.utils.fileutils
— File utilities¶The module boing.utils.fileutils
provides some useful classes
for having a standard interface over standard device files, Unix
non-standard files and tty devices.
It has also been created because it was necessary a more extensive support to the Unix non-standard devices (i.e. named pipes and device files) rather the one provided from the Qt framework.
boing.utils.fileutils.
IODevice
(fd, parent=None)¶The IODevice
provides a standard interface for the
generic file descriptors fd, which it could be a file, the
stdin or the stdout.
bytesWritten
¶Signal emitted when a write operation has been effectuated.
aboutToClose
¶This signal is emitted when the device is about to close. Connect this signal if you have operations that need to be performed before the device closes (e.g., if you have data in a separate buffer that needs to be written to the device).
fd
()¶Return the device’s file descriptor.
isatty
()¶Return True
if the stream is interactive (i.e., connected to a
terminal/tty device)
isOpen
()¶Return whether the device is open.
isTextModeEnabled
()¶Return whether the device provides unicode string rather than bytestream using the read methods.
bytesToWrite
()¶Return the number of bytes that are waiting to be written.
flush
()¶Flush the write buffers of the stream if applicable. This does nothing for read-only and non-blocking streams.
close
()¶Flush and close this stream. This method has no effect if
the file is already closed. Once the file is closed, any
operation on the file (e.g. reading or writing) will raise a
ValueError
.
read
(size=io.DEFAULT_BUFFER_SIZE)¶Read and return size bytes, or if n is not given or negative, until EOF or if the read call would block in non-blocking mode.
readline
(limit=-1)¶Read and return one line from the stream. If limit is specified, at most limit bytes will be read.
The line terminator is always b'\n'
for binary files; for
text files, the newlines argument to open()
can be used
to select the line terminator(s) recognized.
readall
()¶Read and return all the bytes from the stream until EOF, using multiple calls to the stream if necessary.
seek
(offset, whence=io.SEEK_SET)¶Change the stream position to the given byte offset. offset is interpreted relative to the position indicated by whence. Values for whence are:
SEEK_SET
or 0
– start of the stream (the default);
offset should be zero or positiveSEEK_CUR
or 1
– current stream position; offset may
be negativeSEEK_END
or 2
– end of the stream; offset is usually
negativeReturn the new absolute position.
write
(data)¶Write the given bytes or bytearray object, b and return the
number of bytes written. It also emit the signal
bytesWritten
.
The IODevice
class also defines the following constants:
ReadOnly
¶WriteOnly
¶ReadWrite
¶Append
¶boing.utils.fileutils.
CommunicationDevice
(fd, parent=None)¶Specific class for devices for which the readyRead signal is usefull, like for example Unix not regular files and stdin. TcpSocket and UdpSocket do not inherit this class because they inherit specific Qt classes.
readyRead
¶This signal is emitted once every time new data is available for reading from the device. It will only be emitted again once a new block of data has been appended to your device.
boing.utils.fileutils.
File
(url, mode=IODevice.ReadOnly, uncompress=False, parent=None)¶File
instances represent a single file or directory.
boing.utils.fileutils.
CommunicationFile
(url, mode=IODevice.ReadOnly, parent=None)¶CommunicationFile
instances are used to access to
Unix non-standard files. The argument url defines the path
to the file to represent. mode can be set to:
boing.utils.fileutils.
FileReader
(url, mode=IODevice.ReadOnly, uncompress=False, parent=None)¶The FileReader
can be used to read regular files along the
event loop. When the method start()
is invoked, the
FileReader
will trigger the readyRead
signal and
it will repeat it every time the read method is invoked.
readyRead
¶This signal is emitted once every time new data is available for reading from the device. It will only be emitted again once a new block of data has been appended to your device.
completed
¶Signal emitted when the file has been completed.
start
()¶Start reading the file.
boing.utils.querypath
— A query path language¶The module boing.utils.querypath
provides Querypath, a query
language that can be used to handle hierarchical data structures, no
matter if they are composed of standard containers, e.g. list
or dict
, or instances of standard or custom classes.
The proposed querypath language is a derivation of JSONPath, which
was proposed by Stefan Goessner to handle JSON structures. Beyond
some minor changes, the Boing‘s query language exploits the fact
that the attributes of Python class instances are stored inside the
attribute __dict__
, by actually treating the instances of not
Container classes as they were dictionaries.
The root object is indexed using the character $
, but it can be
omitted.
Querypath expressions can use the dot–notation:
contacts.0.x
or the bracket–notation:
['contacts'][0]['x']
or a mix of them:
contacts[0][x]
Querypath allows the wildcard symbol *
for member names and
array indices, the descendant operator ..
and the array slice
syntax [start:end:step]
.
Python expressions can be used as an alternative to explicit names or
indices using the syntax [(<expr>)]
, as for example:
contacts[(@.__len__()-1)].x
using the symbol @
for the current object. Also consider that
built-in functions and classes are not available. Filter expressions
are supported via the syntax [?(<boolean expr>)]
as in:
contacts.*[?(@.x<10)]
In order to access to multiple items that have the same parent, it is
possible to use the operator ,
, as in:
props.width,height
while for selecting multiple items that have different parents, it is
necessary to combine two Querypaths using the operator |
, as
in:
props.*|contact..x
Note that the ,
structure is normally quicker than the |
structure, since in the latter case the query always restarts from the
root object. Indexing all the values of the data model is possible
using the path ..*
.
The module boing.utils.querypath
provides a set of static
functions for executing Querypath expression on user data
structures. The query expression must be provided as a standard string.
boing.utils.querypath.
get
(obj, path)¶Return an iterator over the obj‘s attributes or items matched by path.
boing.utils.querypath.
set_
(obj, path, value, tocopy=False)¶Set the value of obj indexed by path to value. Return obj
if tocopy is False
, otherwise the copy of obj where the
modification is applied.
Note
This function must be used carefully since it is supposed to set the requested property to all the matched objects of the structure even if they do not own such property. A common procedure is require to set a specific property only for the objects that already own such property. As an example:
>>> tuple(querypath.items(table, "..x"))
(('contacts.0.x', 100), ('contacts.1.x', 500))
>>> querypath.set_(table, "..*[?(@.x)].x",10)
<test.Surface object at 0xa2732ac>
>>> tuple(querypath.items(table, "..x"))
(('contacts.0.x', 10), ('contacts.1.x', 10))
Note
The function set_()
does not accepts the querypaths
"$"
and ""
.
boing.utils.querypath.
paths
(obj, path)¶Return an iterator over the paths that index the obj‘s attributes or items matched by path.
boing.utils.querypath.
items
(obj, path)¶Return an iterator over the pairs (path, value) of the obj‘s items that are matched by path.
boing.utils.querypath.
test
(obj, path, wildcard=NOWILDCARD)¶Return whether at least one obj‘s attributes or items is matched by path. The object wildcard matches even if path does not completely match an item in obj.
boing.utils.querypath.
NOWILDCARD
¶Option specifing that the method test()
should not consider
any wildcard.
>>> class Contact:
... def __init__(self, x, y):
... self.x = x
... self.y = y
... def polar(self):
... return math.sqrt(x*x, y*y), math.atan2(y,x)
... def __repr__(self):
... return "Contact(%s,%s)"%(self.x, self.y)
...
>>> class Surface:
... def __init__(self):
... self.contacts = []
... self.props = {}
...
>>> table = Surface()
>>> table.props['width'] = 800
>>> table.props['height'] = 600
>>> table.props['id'] = "mytable"
>>> table.contacts.append(Contact(100,200))
>>> table.contacts.append(Contact(500,600))
>>> tuple(querypath.get(table, "contacts.0.x"))
(100,)
>>> tuple(querypath.get(table, "contacts.*.x"))
(100, 500)
>>> tuple(querypath.get(table, "props.width,height"))
(600, 800)
>>> tuple(querypath.get(table, "..y"))
(200, 600)
>>> tuple(querypath.get(table, "contacts.*[?(@.x<=100)]"))
(Contact(100,200),)
>>> tuple(querypath.get(table, "contacts.*.x,y|props.*"))
(600, 500, 800, 200, 100, 600, "mytable")
>>> querypath.set_(table, "contacts.*.x", 10)
<test.Surface object at 0x8b2606c>
>>> tuple(querypath.get(table, "contacts.*.x"))
(10, 10)
>>> tuple(querypath.paths(table, "props.*"))
('props.height', 'props.width')
>>> tuple(querypath.items(table, "contacts.*"))
(('contacts.1', Contact(100,200)), ('contacts.2', Contact(500,600)))
>>> querypath.test(table, "props.dpi")
False
>>> querypath.test(table, "contacts.*[?(@.x>100)]")
True
>>> querypath.test(table, "props.width.mm")
False
>>> querypath.test(table, "props.width.mm", wildcard=800)
True
QPath
class¶Since Querypath strings must be pre-processed in order to be
executed, supposing you are going to use the same query multiple
times, it may be better to create a QPath
instance, and then
use the member methods, instead of the boing.utils.querypath
static functions. The proposed functuality is equal, but the string
does not have to be pre-processed for all the executions.
boing.utils.querypath.
QPath
(path)¶A compiled Querypath expression.
get
(obj)¶Return an iterator over the obj‘s attributes or items matched by this QPath.
set
(obj, value, tocopy=False)¶Set the value of obj indexed by this QPath to value. Return
obj if tocopy is False
, otherwise the copy of obj
where the modification is applied.
paths
(obj)¶Return an iterator over the paths that index the obj‘s attributes or items matched by this QPath.
items
(obj)¶Return an iterator over the pairs (path, value) of the obj‘s items that are matched by this QPath.
test
(obj, wildcard=NOWILDCARD)¶Return whether this QPath matches at least one obj‘s attributes or items. The object wildcard matches even if path does not completely match an item in obj.
Usage example:
>>> query = querypath.QPath("contacts.*.x")
>>> tuple(query.get(table))
(100, 500)
>>> query.set(table, 10)
<test.Surface object at 0xa2732ac>
>>> tuple(query.paths(table))
('contacts.0.x', 'contacts.1.x')
>>> tuple(query.items(table))
(('contacts.0.x', 10), ('contacts.1.x', 10))
>>> query.test(table)
True
QPath
instances can be combined using the +
operator. This operation concatenates the operand strings using the
|
delimiter, but it also tries to optimize the result by
avoiding expression duplicates, as in:
>>> querypath.QPath("props")+querypath.QPath("contacts")
QPath('contacts|props')
>>> querypath.QPath("props")+querypath.QPath("props")
QPath('props')
Still it cannot optimize more complex overlaps:
>>> querypath.QPath("contacts[0]")+querypath.QPath("contacts.*")
QPath('contacts[0]|contacts.*')
boing.utils.url
— Uniform Resource Locator¶The module boing.utils.url
mainly provides the class
URL
, which is used to represent a Uniform Resource Locator.
Examples:
>>> from boing.utils.url import URL
>>> url = URL("ftp://paolo:pwd@localhost:8888/temp?key=value#frag")
>>> url.scheme
'ftp'
>>> url.site.user
'paolo'
>>> url.site.password
'pwd'
>>> url.site.host
'localhost'
>>> url.site.port
8888
>>> str(url.path)
'/temp'
>>> url.query['key']
'value'
>>> url.fragment
'frag'
URL
¶boing.utils.url.
URL
(string)¶An instance of the class URL
represents an Uniform
Resource Locator (URL). The attribute string of the class
constuctor defines the URL that the instance will represent; at the
instance initialization, string is parsed in order to detect the kind
of URL and to separate it into the specific components: schema,
site, path, query, fragment.
Warning
To address a Windows drive it is necessary to put
an additional /
(slash punctuation) before the drive
letter. Example:
>>> URL("file:///C:/Users/")
Each instance owns the following read-only attributes:
kind
¶Kind of URL. It equals to one of the following:
URL.EMPTY
— empty URLURL.OPAQUE
— URL like <scheme>:<opaque>
URL.GENERIC
— URL like <scheme>://<site>/<path>?<query>#<fragment>
URL.NETPATH
— URL like //<site>/<path>?<query>#<fragment>
URL.ABSPATH
— URL like /<path>?<query>#<fragment>
URL.RELPATH
— URL like <path>?<query>#<fragment>
scheme
¶URL scheme defined by a str
.
fragment
¶URL fragment defined by a str
.
opaque
¶if the URL is of kind URL.OPAQUE
it defines the right part of the URL;
otherwise it is set by default to the empty string ""
.
The string representation of an URL
instance is normally
equal to the string passed at the instance initialization, but
there are few exceptions:
>>> str(URL("udp://:3333"))
'udp://:3333'
>>> str(URL("udp://:3333:0"))
'udp://:3333'
>>> str(URL("file:/tmp/log"))
'file:///tmp/log'
URL
instances are equal if their string representation is
the same:
>>> URL("udp://:3333")==URL("udp://:3333")
True
>>> URL("udp://:3333:0")==URL("udp://:3333")
True
URL
instances can be compared to str
objects:
>>> URL("udp://:3333")=="udp://:3333"
True
and they can be concatenated as they were str
objects:
>>> url = URL("udp://:3333")
>>> "osc."+url
'osc.udp://:3333'
>>> url+"#frag"
'udp://:3333#frag'
Note that the result is a str
, not an URL
instance.
boing.utils.url.
URL_site
(string)¶Used to store the component site of an URL. Each instance owns the following attributes:
user
¶User defined by a string.
password
¶Password defined by a string. It is NOT encripted.
host
¶Site host defined by a string.
port
¶Port number defined by an integer. It defaults to 0
.
Usage example:
>>> url = URL("ftp://paolo:pwd@localhost:8888")
>>> url.site
URL_site('paolo:pwd@localhost:8888')
>>> print(url.site)
paolo:pwd@localhost:8888
>>> url.site.user
'paolo'
>>> url.site.password
'pwd'
>>> url.site.host
'localhost'
>>> url.site.port
8888
Instances can be compared to str
objects:
>>> url = URL("udp://localhost:3333")
>>> url.site=="localhost:3333"
True
and have Boolean value to True
if anyone of the component
attributes is defined:
>>> bool(URL("udp://localhost:3333").site)
True
>>> bool(URL("udp://").site)
False
Warning
Pay attention to the default case:
>>> bool(URL("udp://:0").site)
False
boing.utils.url.
URL_path
(string)¶Used to store the component path of an URL. Usage example:
>>> url = URL("file:///tmp/log")
>>> url.path
URL_path('/tmp/log')
>>> print(url.path)
/tmp/log
>>> url.path.isAbsolute()
True
isAbsolute
()¶Return wheter the path is absolute:
>>> URL("file:///tmp/log").path.isAbsolute()
True
>>> URL("/tmp/log").path.isAbsolute()
True
>>> URL("file").path.isAbsolute()
False
>>> URL("./file").path.isAbsolute()
False
Instances can be compared to str
objects:
>>> url = URL("file:///tmp/log")
>>> url.path=="/tmp/log"
True
and have Boolean value to True
if the URL path is not empty:
>>> bool(URL("file:///tmp/log").path)
True
>>> bool(URL("/").path)
True
>>> bool(URL("udp://:8888").path)
False
Warning
Pay attention to the default transformation:
>>> str(URL("file:/tmp/log"))
'file:///tmp/log'
boing.utils.url.
URL_query
(string)¶Used to store the component query of an URL. This class implements the
collections.MutableMapping
ABC. It is also able to encode the
URL’s query into a “percent-encoded” string.
Examples:
>>> url = URL("udp://:8888?name=Jérémie&connect")
>>> url
URL('udp://:8888?name=J%e9r%e9mie&connect')
>>> url.query
URL_query('name=J%e9r%e9mie&connect')
>>> url.query['name']
'Jérémie'
>>> dict(url.query)
{'name': 'Jérémie', 'connect': ''}
>>> URL("udp://:8888?name=Jérémie&connect")==URL("udp://:8888?name=J%e9r%e9mie&connect")
True
It is possible to use the structure <!...!>
in order to assign
to the query key or value a string that contains the characters
&
or =
, which is normally not permitted since it would
break the URL syntax.
Example:
>>> url = URL("edit:?<!..contacts.*[?(@.id>=2)]!>=<!True&False!>&k2=v2")
>>> dict(url.query)
{'k2': 'v2', '..contacts.*[?(@.id>=2)]': 'True&False'}
Warning
The characters #
and ?
cannot be used inside the
<!...!>
structure.
The easiest way to get help with the project is to join the mailing list boing@librelist.com.
The archive of the mailing list can be checked out at http://librelist.com/browser/boing/ .
The other good way is to open an issue on github.
v0.3.1, Sep, 18 2012 — Fixed and upgraded the installation guide.
boing.config
for storing global configuration variables.--no-gui
for running Boing without a display server..ui
files are automatically compiled when the package is installed.boing.VERSION
changed into boing.__version__
boing.utils.url
.v0.3.0, Sep, 10 2012 – First public release.
v0.2.0, Apr, 1 2012 – Architetecture wide modifications.
v0.1.0, Jan, 1 2012 – Pre-release.
Source: http://www.gnu.org/licenses/gpl-2.0.txt
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
Bugs and unittest:
Class DataWriter
should signal an error when it is
demanded to write to a closed device.
Add boing.test.nodes.test_loader
cases for the data
processing nodes.
Add docs and unit test for class boing.utils.url.URL
on
MS Windows.
Improve class QPath
: regular
expression compilation, join method, add unittest. The command:
QPath.filter(boing.nodes.encoding.TuioDecoder.getTemplate(), "diff.*.contacts.*.rel_pos")
raises an error if QPath._iterProp()
returns a real
iterator.
when boing.create()
raises an exception, it shows the lower
URI and not the original one. This may be misleading for users.
The player:
‘s playlist has some model trouble: when I
drag and drop some files from a folder to the root level before
the folder an Exception is raised. Sometimes files desappears.
Handle when a source has been closed and when to start players (e.g. if TCP socked has been disconnected, TcpServer turned off).
Resolve the UDP socket reuse port issue on Windows.
The structure <!...!>
used in defining not standard
URL
query keys and values does not
work if characters #
or %
are used inside the structure.
Pipeline architecture:
Producer
should also
automatically know whether being active or not, like the class
WiseWorker
does. Check the ‘tag’
structure.Node
shouldn’t be a
QObject?Data model:
quickdict
constructor:
if an hierarchical dictionary is passed to the constructor not all
the hierarchy is transformed to a quickdict.Functionalities:
evdev
and uinput
in&out bridges.libfilter.filtering.signal
to
boing.filtering
.Gesture Recognition:
Docs:
boing.net.tcp
and
boing.net.udp
.Other:
boing.utils.fileutils
should be reengineered.Todo
Describe how to configure the pipeline dinamically
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/boing/checkouts/latest/doc/source/boing.rst, line 101.)
Todo
Comment module boing.filtering
.
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/boing/checkouts/latest/doc/source/boing.filtering.rst, line 29.)
Todo
Improve docs for the module boing.net.tcp
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/boing/checkouts/latest/doc/source/boing.net.tcp.rst, line 24.)
Todo
Improve docs for the module boing.net.udp
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/boing/checkouts/latest/doc/source/boing.net.udp.rst, line 24.)
Todo
Describe the data model.
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/boing/checkouts/latest/doc/source/datamodel.rst, line 24.)
Todo
Describe an example of functional node.
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/boing/checkouts/latest/doc/source/developer.rst, line 57.)
Todo
Speak about the default nodes and the node uris convention. Also add a link to the Nodes reference table.
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/boing/checkouts/latest/doc/source/functionalities.rst, line 24.)
Todo
Describe data logging and replaying (both OSC and JSON formats).
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/boing/checkouts/latest/doc/source/functionalities.rst, line 46.)
Todo
Describe the recorder tool.
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/boing/checkouts/latest/doc/source/functionalities.rst, line 52.)
Todo
Describe the Player tool.
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/boing/checkouts/latest/doc/source/functionalities.rst, line 65.)
Todo
Describe the calibration node.
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/boing/checkouts/latest/doc/source/functionalities.rst, line 81.)
Todo
Describe the filtering module.
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/boing/checkouts/latest/doc/source/functionalities.rst, line 87.)
Todo
Logging utils tutorial
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/boing/checkouts/latest/doc/source/logging.rst, line 24.)
Todo
multi-touch utilities tutorial.
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/boing/checkouts/latest/doc/source/multitouch.rst, line 24.)
Todo
Improve the OSC tutorial.
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/boing/checkouts/latest/doc/source/osc.rst, line 24.)
Todo
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/boing/checkouts/latest/doc/source/pipeline.rst, line 406.)
Todo
Data redirection tutorial
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/boing/checkouts/latest/doc/source/redirection.rst, line 24.)
Todo
Script advanced options tutorial
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/boing/checkouts/latest/doc/source/scriptadvanced.rst, line 24.)
Todo
Improve the TUIO tutorial.
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/boing/checkouts/latest/doc/source/tuio.rst, line 24.)