Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

JVM Languages

Gumbie: A GUI Generator for Jython


Apr02: Gumbie: A GUI Generator for Jython

Automatically putting a pretty face on Jython

Peter is an assistant professor of mathematics at the University of Illinois at Urbana-Champaign. He can be contacted at [email protected].


A Factory with a Twist


Essentially, a menu bar is just a tree. For example, the first few branches of the Netscape Communicator menu bar on my Linux system look like Listing One. If you want to build a menu bar, you should only have to write down such a tree, and the rest ought to be the responsibility of whatever productivity tool you're using. Gumbie, the tool I present in this article, does just that — automatically creates menu bars. Additionally, Gumbie provides a number of other high-level features that facilitate the creation of graphical user interfaces (GUIs).

As I created Gumbie primarily for the purpose of building GUIs for existing software in Java, I implemented Gumbie in Jython (http://www.jython.org/) — a 100 percent pure Java implementation of Python that makes Java scriptable. Consequently, Gumbie works on any system with the Java Development Kit 1.1.7 (or greater). I have tested it under Linux, Solaris, Windows, and MacOS 9. Gumbie, which I'm releasing under the GNU Public License, is available at http://www.math.uiuc.edu/~brinkman/software/gumbie/ and electronically from DDJ; see "Resource Center," page 5.

A Gumbie Overview

Most of the programming I do involves mathematical software (see, for instance, http://www.math.uiuc.edu/~brinkman/software/train/) that works as a UNIX-style filter, reading text from stdin and writing text to stdout without user interaction. To reach more potential users, I decided to equip these filters with a GUI. As the GUI was supposed to be no more than the icing on the cake, I didn't want to spend too much time on it, so I needed a tool that would let me create GUIs as quickly as possible. Gumbie was the result and it has since developed a life of its own.

Table 1 shows the most important modules of Gumbie. The class MenuMaker is the core of the package, providing most of Gumbie's services, such as the creation and maintenance of the GUI. In the pattern language of the Gang of Four (Design Patterns: Elements of Reusable Object-Oriented Software, by Erich Gamma et al., Addison-Wesley, 1994; ISBN 0-201-63361-2), MenuMaker is a mediator — the central hub through which all transactions must pass. Concentrating responsibility in one class makes it easy to attach a GUI to other classes (such as the filters that make up my mathematical software) that may remain blissfully unaware of the GUI that uses them.

MenuMaker also offers a number of other services that I find useful when creating GUIs, such as high-level dialogs and basic bookkeeping (enabling/disabling menu items and buttons, keeping track of the instances of MenuMaker that are currently active, and so on). In most cases, Gumbie users will not work with MenuMaker itself, but rather with a subclass that inherits from MenuMaker and from some kind of frame (like java.awt.Frame). PowerFrame and LayeredFrame are examples of such mixed classes.

The LayeredFrame class provides a way of managing several objects derived from MenuMaker within one frame, provided they conform to a certain interface. The class TextLayer implements this interface and offers a basic set of methods for dealing with text. Layered frames save valuable real estate on the screen and provide a way of simulating pipes within a GUI — you simply take the contents of one layer, feed them into a filter, write the output of the filter to the next layer, and so on. Not only does this lower the bar for users who are unfamiliar with the UNIX command line, but it also takes the command-line paradigm to pipeless systems such as MacOS 9.

Gumbie maintains a widget factory object (after Gang of Four, see the text box "A Factory with a Twist") that is responsible for creating the concrete widgets that make up the GUI. The current version of Gumbie comes with a widget factory that creates awt components because I decided to minimize the requirements for running my software. Many systems still seem to ship with Java 1.1, and many potential users of my mathematical software might lose interest if they had to upgrade their system first.

To switch to a different family of widgets (for example, from awt to Swing), you only have to change the factory. Moreover, the current widget factory is so straightforward that the migration from awt to Swing should require little more than search-and-replace operations. The factory module also defines high-level dialogs. Replacing the default dialogs with customized versions merely amounts to subclassing or replacing the dialog classes, and subclassing the factory so it creates the new dialogs.

In addition to the run-time modules, Gumbie includes a code generator that reads descriptions of GUIs and creates subclasses of MenuMaker or PowerFrame that realize these GUIs. Finally, Gumbie is ready for internationalization — all the strings containing messages for users are wrapped in such a way that they can easily be replaced by translations into different languages (this involves Python's gettext module, see http://www.python.org/doc/current/lib/node192.html).

The GUI Description Format

For Gumbie, a GUI consists of a menu bar and up to four (possibly empty) tool bars, one for each point of the compass. Typically, such a GUI lives inside a frame equipped with a BorderLayout whose peripheral regions hold the tool bars and whose central region is reserved for contents supplied by the programmer. While this format imposes certain restrictions on the appearance of the GUI, it is a small price to pay for the resulting simplification of GUI descriptions, and the format has always been general enough for my purposes.

The Gumbie code generator (generator.py) creates Jython programs from GUI description files. Listing Two shows the overall structure of such a GUI description, and Table 2 gives a detailed grammar of the various entries (see Listing Three for an example). Indentation matters, much like in Python programs. All the components are optional, and generator.py reverts to sensible defaults for missing components. The order of the four tool bars is arbitrary and only affects the order in which the tab key transfers the keyboard focus from one button to the next.

Whitespace (spaces and tabs) are only allowed to provide indentation or separate entries within a line; in particular, labels and options may not contain whitespace. If you want a label that contains spaces, use underscores ("_") instead, and Gumbie will replace them by spaces later on.

The GUI description format lets you enter essential information about the GUI (such as the structure of the GUI, labels of buttons, and callback methods) with a minimum amount of typing. At the same time, it is fairly general because the extra option lets you define arbitrary widget properties, taking advantage of Jython's ability to pass JavaBean properties to class constructors as keyword arguments. For example, when working with a widget factory that creates Swing components, one might add an icon to a button by adding an option like extra:{'icon':someIcon} to the definition of the button. Gumbie does not impose any restrictions on the keyword arguments created by the extra option, and you are responsible for making sure that the widget factory knows how to handle them.

The Resulting Code

Feeding a GUI description into generator.py yields a functional Jython program. In particular, all the buttons and menu items are already equipped with callback methods — generator.py automatically creates callback methods where the GUI description doesn't provide them. These automatically generated methods merely acknowledge the event that invoked them, so they need to be manually modified to give them some useful functionality.

The generated code also contains a copy of the original GUI description, and a GUI can be modified by editing its description in the generated code and feeding the edited code into generator.py once more. To avoid destroying manual modifications of the code, generator.py recognizes certain environments within the code and preserves their contents as much as possible.

There are, for example, environments for callback methods, imports, and the like. Table 3 lists all possible environments. Contents of environments are changed or lost only if this is inevitable; for example, changing the base class will require a new constructor, and a callback method only makes sense as long as there exists a corresponding widget. When generator.py has to change or overwrite the contents of an environment, it still includes the old contents in a nearby comment. Manual modifications should only occur inside some environment, or else generator.py will ignore them.

Gumbie in Action

For an example of Gumbie in action, consider implementing a simple text editor. You first need to decide which Gumbie class to work from. In this case, TextLayer is the natural choice as it is a subclass of MenuMaker that comes equipped with a text area and a set of methods for rudimentary text manipulation. Listing Three shows a possible menu structure for such a text editor, complete with menu items for search and replace operations. TextLayer lets users check in the current version of the document and maintains a list of checked-in versions, with its undo/redo methods operating on this list.

Writing the contents of Listing Three to a file, say SimpleEditor.gui, and typing jython generator.py -o SimpleEditor.py SimpleEditor.gui at the command line creates a Jython program that you can execute by typing jython SimpleEditor.py; see Figure 1.

Since the GUI description already links most menu items to existing callback methods in TextLayer, the editor lets you write text, save it to a file, undo changes, and the like. Only the menu items for search-and-replace operations still require some work. At the moment, clicking on one of them merely opens a dialog window that acknowledges the event. To make the search/replace operations work, you only need to modify the corresponding callback methods that generator.py created.

Listing Four shows the automatically generated callback method for the search method. Listing Five is a modified callback method that takes advantage of the various services that MenuMaker offers, such as dialogs for entering strings or displaying short messages. After you change the remaining two callback methods in a similar fashion, the editor is fully functional.

The contents of the Help window and the About dialog are still set to their default values, so you adjust them by entering the appropriate messages in the init environment. Listing Six shows the automatically generated environment, while Listing Seven shows the same environment with the necessary changes. That's it.

When adding a GUI to existing filters, I usually start with a simple editor like that presented here and add a menu or tool bar that lets users have the filter modules operate on the current text.

Gumbie and the Rest of the World

Gumbie is not the first tool for creating GUIs. Parrot (http://www.vision25.demon.co.uk/oss/parrot/intro.html), for example, serves a similar purpose as generator.py, creating GUIs in Python/Tk (as well as HTML and XML) from a textual description. In spite of this similarity, however, Gumbie and Parrot differ in their philosophy. Parrot aims to provide a way of creating GUIs in a variety of languages based on the same description file. Gumbie, on the other hand, focuses on Jython, but offers services beyond the initial creation of the UI.

To my knowledge, Gumbie is currently the only GUI builder for Jython, although I expect it will not be the last, as Jython seems ideally suited for the creation of Java GUIs. Jython does cause a certain performance penalty (mostly at startup), but the resulting delays are usually barely noticeable, while the improvements in productivity are dramatic.

While Gumbie is considerably more modest in its aim and scope than large packages such as Borland's JBuilder (http://www.borland.com/jbuilder/), OpenStep/GNUstep (http://www.gnustep.org/), or Glade (http://developer.gnome.org/arch/devel/glade.html), it has its place when it comes to rapid application development. I am not aware of any faster way to create a fully functional GUI.

Conclusion

The current version of Gumbie does everything I originally wanted it to do. Nevertheless, I can see a number of useful future developments, the most immediate one being a factory for Swing components. Moreover, as none of the generic classes of Gumbie use Java classes, one might even build a factory that creates Tk widgets, resulting in programs that work interchangeably with Jython/Java or Python/Tk.

A large library of high-level GUI primitives (including, for example, a collection of dialogs for different purposes) would be another valuable addition, as would be a generalized description format for GUIs that consist of more than the current set of menus and tool bars.

In spite of its general "No bells and whistles" approach, Gumbie is highly extensible. If you want to extend Gumbie for your purposes, please do so.

Acknowledgments

The name "Gumbie" refers to those lovable rubber-booted characters from Monty Python's Flying Circus. I changed the spelling from "gumby" to "Gumbie" so it contains the letters G, U, and I. The class MenuMaker was originally based on the class GuiMixIn from the book Programming Python, by Mark Lutz (O'Reilly & Associates, 1996), although there is hardly any resemblance in the current version. Gumbie exposes a bug in Apple's Java Runtime for Macs (MRJ). I am indebted to Finn Bock for his help in finding a workaround that makes Gumbie work on Macs.

DDJ

Listing One

# Menu bar for Netscape Communicator (excerpt)
File
    New
        Navigator Window
        New Message
        ...
    Open Page
    Save Frameset as
    ...
Edit
    Undo
    Redo
    ...

Back to Article

Listing Two

# Overall structure of GUI descriptions
# begin gui
# comments are allowed at any point
Name
    NameOfClass
Type
    NameOfSuperclass
Init
    InitializationPackage
Menubar
    (MenuDefinition)*
Toolbar
    N
        (ButtonDefinition)*
    E
        (ButtonDefinition)*
    W
        (ButtonDefinition)*
    S
        (ButtonDefinition)*
#end

Back to Article

Listing Three

# A simple editor
# begin gui
Name
    SimpleEditor
Type
    TextLayer
Menubar
    File
        Open    self.load   shortcut:o
        Save    self.save   enabled:self.notEmpty() shortcut:T
        ---            # --- stands for a separator
        Exit    self.exit   shortcut:X
    Edit    tearoff
        Clear   self.clear    enabled:self.notEmpty()
        Undo    self.undo   enabled:self.canUndo()
        Redo    self.redo   enabled:self.canRedo()
        ---
        Find        enabled:self.notEmpty()         shortcut:f
        Find_again  enabled:self.searchString!=None shortcut:F
        Replace     enabled:self.notEmpty()         shortcut:r
    Help
        Help_window self.help   shortcut:h
        About       self.about
Toolbar
    S
        Check_in    self.checkIn    enabled:self.hasChanged()
#end

Back to Article

Listing Four

#Automatically generated call-back function
#begin callback menuCallback_Find
    def menuCallback_Find(self,e=None):
        # callback for 'Find'
        self.infoBox('Find','You clicked on Find!','OK',0)
#end

Back to Article

Listing Five

#Manually modified call-back function
#begin callback menuCallback_Find
    def menuCallback_Find(self,e=None):
        s=self.inputString('Find',
            'Enter search string:')
        if s!=None:
            if not self.find(s):
                self.infoBox('Not found',
                    'String '+s+' not found.')
#end

Back to Article

Listing Six

#Automatically generated init environment
#begin init
# put class level variables here
#end

Back to Article

Listing Seven

#Modified init environment with useful messages
#begin init
    helptext='Nobody expects the Spanish inquisition.'
    version='SimpleEditor 0.1'
#end

Back to Article


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.