ROS and Gtk for Laboratory Control
At the lab in which I work (Andrew Straw, strawlab) we study the visual flight behaviour of Drosophila using virtual reality. The implementation of this will be explained in future posts and papers however for this post I am going to describe how I used Gtk1 and ROS to build an interface to control and monitor running experiments (called the 'Operator Console').
A future post will address and release all the ROS+GObject2 glue that lets these interfaces scale dynamically as nodes (dis)appear. This just shows the relevant Gtk parts and has some comments on what I would like from Gtk to make these sort of interfaces easier.
The screenshow shows the first tab of the 'Operator Console'3.
Implementation Notes
- I use the secondary icon support of
Gtk.Entry
to show the contents contain sensible data. Maybe validation support in Gtk would be useful here bug. - The 'Description' entry is a
Gtk.TextView
, not aGtk.Entry
. It was necessary to apply custom CSS to make it look reasonably similar. Sadly, it does not support the full/same set of CSS properties asGtk.Entry
, so it was impossible to show the same border radius and focus colors bug. Perhaps a multi-lineGtk.Entry
would be better. - The bottom half of the window shows the utilisation of all computers. I tried a few versions of this, and simple sensibly formatted monospaced text looked much better than anything else I tried. Any suggestions?
This screenshot shows an example screen where we mix the control and monitoring or many instances of the same ROS node.
Implementation Notes
- The
Gtk.Switch
simultainously displays the status of the projector, and also allows control of the node. The is a common use-case in the software, and due to the asynchronous nature of the ROS messages, I need to distinguish these from user-generated signals. I have wrappers such as the following for many widgets4. Advice on how to distinguish this use-case would be preferred.
class UpdateableGtkSwitch(Gtk.Switch): def __init__(self, *args, **kwargs): Gtk.Switch.__init__(self, *args, **kwargs) self._changing = False self.connect_after("notify::active", self._changed) def _changed(self, *args): if self._changing: self.stop_emission("notify::active") def set_active(self, is_active): self._changing = True Gtk.Switch.set_active(self, is_active) self._changing = False def connect(self, *args, **kwargs): self.connect_after(*args, **kwargs)
- The "Standby (Computer1)" is for display only and mirrors the status of a ROS
topic. I would like some way visually to inicate that this widget is not actually
an editable
Gtk.Entry
. Currently theGtk.Entry
is seteditable = False
, it looks to out of place withsensitive = False
. Perhaps I should add some customCSS
to color it slightly different. Suggestions are appreciated.
Closing Remarks
I'm really happy with the status of the PyGObject
bindings. We have a few quite
large applications built using them (and ROS) and I have no complaints about
performance5 or otherwise. The conventional wisdom was that PyGTK (and GTK) were
not suitable for threaded workloads but the threading model of ROS guarentees that
the 'operator-console' shown above manages upwards of 50 background threads asynchronously
updating the GUI state.
-
Actually PyGObject, argh why didn't we keep the name as pygtk? ↩
-
I'll blog about this later. For the curious, rosgobject lives here. ↩
-
In real operation this GUI shows the state of many more machines/nodes/computers. This screenshot is running on my laptop because showing too much more might give away the game ;-). ↩
-
freeze_notify
andthaw_notify
would almost work, if the events could be dropped and not queued. Also, not all widgets usenotify::active
,GtkComboBox(Text)
for example. A general way to do this would be preferred. ↩ -
Excluding plotting / graphing performace. But that is fodder for a later post. ↩