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.
- I use the secondary icon support of
Gtk.Entryto show the contents contain sensible data. Maybe validation support in Gtk would be useful here bug.
- The 'Description' entry is a
Gtk.TextView, not a
Gtk.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 as
Gtk.Entry, so it was impossible to show the same border radius and focus colors bug. Perhaps a multi-line
Gtk.Entrywould 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.
Gtk.Switchsimultainously 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
Gtk.Entry. Currently the
editable = False, it looks to out of place with
sensitive = False. Perhaps I should add some custom
CSSto color it slightly different. Suggestions are appreciated.
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? ↩
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 ;-). ↩
thaw_notifywould almost work, if the events could be dropped and not queued. Also, not all widgets use
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. ↩