aluntzer / gtknodes

A GTK-based library to create functional flow graphs with the ability to pass arbitrary data between connected elements.
Other
92 stars 10 forks source link

GtkNodes

This library provides a (hopefully) easy-to-use interface for the creation of functional flow graphs with the ablility to pass arbitrary data between connected elements.

The flow graph is managed in NodeView, which is a graphical connection manager for the graph's functional element (Nodes). Nodes are custom GtkWidget containers (derived from GtkBox), which hold one or more items (i.e. other widgets). When a widget is added to a node, it will be assigned a NodeSocket, which represents (and manages) the actual connectivity between nodes.

A NodeSocket can be configured in one of 3 modes: SINK, SOURCE and DISABLE. SINKs accept a single input and are connected to a SOURCE by a drag-and-drop operation from a source socket to a sink socket. SOURCEs can have an arbitrary number of connected outputs. A socket in SINK mode will be displayed vertically centered to its associated widget and on the left edge of the node, while sockets in SOURCE mode are drawn on the right edge of the node. Sockets in mode DISABLE are simply not drawn.

Existing connections can be modified in the NodeView by drag and drop. If a connected sink receives a new connection from a source, it will disconnect from its previous source and connect to the new input. If a drag operation is started on top of a connected sink, its source will be disconnected and the drag event will be redirected to the source, just as if the user had initiated the drag event on the source. This method is also how the user would drop an existing connection between nodes: by starting the drag on the sink and then simply aborting the drag.

In order to ensure (data format) compatbility between nodes, a numerical key can be configured. If the key changes, or a connection to a socket with an incompatible key is attempted, the connection will be either dropped or rejected. A key value of 0 for sinks is special, in that they will accept any input.

In order to visualize compatible sockets, an RGBA value can be set.

Nodes

The Node widget is a widget container drived from #GtkBox. Widgets added to the node are assigned a NodeSocket. The user must configure the type of socket and connect to the socket-incoming signal in order to be able to receive the data passed through a connection.

The Node can be collapsed by clicking the #GtkExpander which will hide the node items, and show a compact representation of the node with just the GtkExpander and the sockets visible.

NOTE: When adding a node to the NodeView, make sure to call gtk_widget_show_all() on the Node widget to show the widget along with the content. This was changed from the previous behaviour where gtk_nodes_node_view_add() called show_all() on the NodeView, leading to undesirable effects when adding new nodes.

Data Exchange

To receive data on a socket, connect to the "socket-incoming" signal just as you would for any other Gtk/Glib signal.

All data are passed encapsulated in a GByteArray in order to accomodate arbitrary formats and also so that Nodes can be introspected and used with other languages.

To write data on a socket, a function is provided which requires the socket reference (can be retrieved when adding an item to a Node) and a GByteArray as input.

Ability to save and restore graphs

All reconstruction of graphs is handled by GtkBuilder. This means that all individual nodes must be implemented as subclasses of the Node widget, so GtkBuilder can locate, create and add them to a node view.

The NodeView widget provides load/store functions for this purpose.

Internal states

Custom GtkNodesNode widgets can save and restore their internal child widget states and other properties and special tags by implementing the proper GtkBuildable interfaces. To export the configuration, node_export_properties() must be implemented and is expected to return valid xml output which is integrated into the xml output produced by #GtkNodesNodeView.

For example, to restore the value of an internal spin button widget, the function would return an allocated string containing:

<child internal-child="spinbutton">
     <object class="GtkSpinButton">
        <property name="value">5</property>
     </object>
</child>

The weird thing is that GtkBuilder apparently wants to add the internal children to the GtkNode widget, even though the "internal-child" property is set. This obviously fails, resulting in a warning message, however the restoration of the child's property succeeds. I have not yet figured out, why this is the case, maybe this requires another builder class function to be implemented, or I may have misunderstood the intention of "internal-child".

Example nodes demonstrating this functionality are found in examples/nodes/node_step.c for the C demonstrator and in class NumberNode for the Python demo (examples/img.py).

API

You can find a gtkdoc rendering of the API here.

Screenshots

From the C demo: an 8-bit binary adder with 9 bits of output

BinAdder

The Python example: applying image filters using NumPy, SciPy and Matplotlib plus a random calculator node controlling plot parameters

ImgFilter

An actual use case: freely programmable observations for my radio telescope control software. The nodes integration is only preliminary, but what can already be constructed very easily in this example is a grid observation program in a celestial coordinate system (Horizon, Equatorial or Galactic) and the application of a filter to the spectral data received by the telescope.

If you want to try it for yourself, see radtel. The nodes support is still incomplete, you can find it on the gtknodes branch.

RadTel

Building

If you want to use this library from python or any other language using gobject introspection bindings, make sure the build tools (g-ir-scanner, g-ir-compiler). If bindings are still not generated, enable introspection explicitly:

./configure --enable-introspection=yes

If you want to try the python example without installing the library system-wide, make sure to export the proper paths, i.e. from the examples/ subdirectory run the demo like this:

GI_TYPELIB_PATH=../introspection/ LD_LIBRARY_PATH=../src/.libs python img.py

Build dependencies

Ubuntu:

sudo apt install \
  gobject-introspection \
  gtk-doc-tools \
  libtool \
  autoconf \
  automake \
  libgladeui-dev \
  libgirepository1.0-dev

Packages

Arch Linux:

Available in AUR as gtknodes-git

Notes

Licensing

GtkNodes is released under the terms of the GNU Lesser General Public License, version 2.1 or, at your option, any later version, as published by the Free Software Foundation.

Please, see the COPYING file for further information.