Widget Development

The rose config-edit GUI displays configurations using built-in widgets. For more complex requirements rose config-edit supports custom widgets as plugins.

In this tutorial we will write a custom widget which offers typing suggestions when entering usernames.

../../../_images/rose-edit-custom-widget.png

Warning

If you find yourself needing to write a custom widget, please contact the Rose team for guidance.

Example

Create a new Rose app by running the following command replacing DIRECTORY with the path in which to create the suite:

rose tutorial widget <DIRECTORY>
cd <DIRECTORY>

You will now have a Rose app which contains the following files:

<DIRECTORY>/
|-- meta/
|   `-- lib/
|       `-- python/
|           `-- widget/
|               |-- __init__.py
|               `-- username.py
`-- rose-app.conf

The rose-app.conf file defines an environment variable called USER:

[env]
USER=fred

The __init__.py file is empty - the presence of this file declares the widget directory as a python package.

The username.py file is where we will write our widget.

Initial Code

We will start with a slimmed-down copy of the class rose.config_editor.valuewidget.text.RawValueWidget which you will find in the file username.py. It contains all the API calls you would normally ever need.

We are now going to extend the widget to be more useful.

Add a line importing the pwd package at the top of the file:

+ import pwd

 import gobject
 import pygtk
 pygtk.require('2.0')
 import gtk

This adds the Python library that we’ll use in a minute.

Now we need to create a predictive text model by adding some data to our gtk.Entry text widget.

We need to write our method _set_completion, and put it in the main body of the class. This will retrieve usernames from the pwd.getpwall() function and store them so they can be used by the text widget self.entry.

Add the following method to the UsernameValueWidget class:

def _set_completion(self):
    # Return a predictive text model.
    completion = gtk.EntryCompletion()
    model = gtk.ListStore(str)
    for username in [p.pw_name for p in pwd.getpwall()]:
        model.append([username])
    completion.set_model(model)
    completion.set_text_column(0)
    completion.set_inline_completion(True)
    self.entry.set_completion(completion)

We need to make sure this method gets called at the right time, so we add the following line to the __init__ method:

  self.entry.show()
+ gobject.idle_add(self._set_completion)
  self.pack_start(self.entry, expand=True, fill=True,
                  padding=0)

We could just call self._set_completion() there, but this would hang the config editor while the database is retrieved.

Instead, we’ve told GTK to fetch the predictive text model when it’s next idle (gobject.idle_add). This means it will be run after it finishes loading the page, and will be more-or-less invisible to the user. This is a better way to launch something that may take a second or two. If it took any longer, we’d probably want to use a separate process.

Referencing the Widget

Now we need to refer to it in the metadata to make use of it.

Create the file meta/rose-meta.conf and paste the following configuration into it:

[env=USER]
widget[rose-config-edit]=username.UsernameValueWidget

This means that we’ve set our widget up for the option USER under the section env. It will now be used as the widget for this variable’s value.

Results

Try opening up the config editor in the application directory (where the rose-app.conf is) by running:

rose config-edit

Navigate to the env page. You should see your widget on the page! As you type, it should provide helpful auto-completion of usernames. Try typing your own username.

Further Reading

For more information, see Rose GTK library and the PyGTK web page.