Rapid Application Development with Python and Glade

Create and modify your Python application's GUI using the easy design tool Glade; then, automate the process of setting up event handlers.

Writing GUI programs involves two basic steps. First, you need to write the code to create the interface, with elements, such as menus, and widgets, such as buttons, labels and entry fields. You then need to write the code that executes when events occur, such as when a button is pressed or a menu item is selected. When the program runs, it enters an event loop that repeatedly waits for an event and then calls the event handler, also known as a callback function, that was defined for that event. For example, you write a function to be called when a button is pressed. Writing code to display the widgets, defining the functions to be called when events occur and connecting each event to the specific function is a tedious process to do by hand.

For many widget sets, programs exist to lay out the GUI visually. Damon Chaplin wrote the Glade program to allow users to create an interface visually using the GTK/GNOME widgets and also to specify which functions to call when events occur. Glade stores the layout of the widgets and the callbacks as an XML file. Glade also generates a C or C++ program that contains all the calls to create the widgets in the specified layout, connect the callbacks and define empty functions for each callback. However, Glade does not create Python code. The GladeGen program I wrote generates Python code based on the Glade XML file.

If you change your GUI using Glade, you need Glade to output the new C/C++ code to create the GUI. This can be annoying, especially if you have modified the code that creates the GUI. James Henstridge wrote libglade to alleviate the need to hard code the GUI-generating code in your program. James also started and helps maintain the GTK/GNOME Python bindings, a Python module that provides access to GTK/GNOME C functions. With libglade, your program does not contain code to create the interface. Instead, libglade parses the XML file when your program is run and creates the interface on the fly at runtime. Thus, whenever you change your GUI using Glade, you do not need to change the code that creates the interface.

I prefer using Python except in cases of code containing a lot of computation where speed is crucial. Python's high-level data structures and interpretative environment make it quick to develop, modify and maintain code. The September 2003 issue of Linux Journal contains an introductory article on PyGTK and Glade (see the on-line Resources section) that readers unfamiliar with Gtk and Glade will find helpful.

The motivation for GladeGen was a patient database/accounting system I was writing for the optometric/optical offices where my wife works. Before my wife started working with them, they were using a Microsoft Access database system someone had written. That person had moved out of state, and the office wanted a new system. They talked to other optical offices around town to find out what software was being used. People at each office complained about the software; the systems were buggy and expensive. I convinced the owner of my wife's office that I could write a custom system during the summer for about the same cost as other new software, and that it would do exactly what they needed it to do. But, he had to let me use Linux.

The end result is a Python/GTK program that uses PostgreSQL as the back-end database to store all of the data. This allows all of the searching and tabulating to be done by SQL commands. The Python code provides the layer of code that communicates between the PostgreSQL database and GTK interface. Both GTK and PostgreSQL are written in C, so they run fast. The Python code is more than fast enough on modern processors for handling the communication between GTK and PostgreSQL. The PyGTK front end and PostgreSQL database allow the client front end to access any database server so they can run the front-end GUI on multiple computers and access the database server. The client/server setup allows them to run the front-end GUI and access the PostgreSQL server at other locations over the Internet through an SSH-encrypted connection. It also allows me to have remote access from home when users have questions about the system.

The program has more than 40 windows, including those for entering patient information, frame/lens purchases, contacts, reconciling insurance payments and entering and tracking the frame inventory. I decided to use Glade to create each window, and because I wanted to use Python, I needed to write my own software to automate code creation for each window. The result is GladeGen.

GladeGen Usage

The code I have written automates the task of creating Python code to create the interface, connect the callback functions, provide access to the widgets and create empty callback functions based on the Glade XML file. Using this software, the steps for creating a GUI program are 1) use Glade to make the interface visually and save the XML file, 2) run GladeGen to generate the code and 3) write the code for the callbacks.

The code GladeGen creates contains all the code to run the application and display your interface, along with empty callback functions. It also allows you to use Glade to modify the interface. When you rerun GladeGen, it regenerates the application code with any additional callbacks and new widgets, without changing or modifying any of the existing code.

Here, I demonstrate how to use GladeGen by creating a math quiz program. The complete program is available from the Linux Journal FTP site (see Resources). GladeGen works with the GTK 2.x widget set and the corresponding version of Glade, which on Red Hat systems is named glade-2. If you are familiar with the GTK widgets, Glade is fairly intuitive to use. If not, you should familiarize yourself with the various container widgets, including table and horizontal/vertical box.

Using glade-2, I created the first version of the interface. I started with a GtkWindow and added a GtkVBox. I placed two GtkFrame widgets in the vertical box and a GtkTable in each frame. All the other widgets are placed in the two table widgets. I used GtkSpinButton widgets to allow the user to select the number of digits and operators in the problem. GtkCheckButton widgets are used to indicate which operators should be included in the problem. GtkEntry widgets are used for the problem, the answer and information on the number of correct/incorrect problems answered. The other widgets are GtkLabel and GtkButton widgets.

I saved the Glade file as mathflash.glade. Glade does not ask if you want to save your file, so you need to remember to save it before quitting if you make any changes to your interface. See Figure 1 for a screenshot of the interface and Glade. It shows the reset_button is configured to call the function on_reset_button_clicked when the button is clicked.

Figure 1. Setting the Callback for a Button in Glade

Glade allows you to give each widget a name or provides a default name. For the widgets we need to interact with, such as buttons and data entry widgets, I provided names that match their intended uses. With Glade, you also specify the signals you want to connect to callback functions. As mentioned above, using Glade, I specified that on_reset_button should be called when the reset_button is clicked.

Next, I used GladeGen to produce template code for the application using the command GladeGen.py mathflash.glade MathFlash MathFlash. The command-line arguments are the name of the Glade XML file, the name of the Python file/module to create and the name of the class to create in that file/module. The resulting MathFlash.py file can be found in Listing 1. The MathFlash class subclasses a GladeWindow class, the class that uses libglade to connect all the callbacks listed in the handlers variable. It also creates a dictionary, self.widgets, that maps each widget name in the widget_list variable to the corresponding GtkWidget instance. The GladeWindow subclass provides default show and hide methods so that the template code can be run immediately to view the interface before you start writing the callbacks.

______________________

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

I have a better solution...

Anonymous's picture

In www.oneminutepython.com I show you how to develop an application in only one minute! It contains direct links to key (all open source) applications (python, wxpython etc.) and a template application.

You saved my day!

sharat87's picture

I am pulling my hair off for almost 4 hours as to why my gtk window is not appearing... it was because I did not write the window.show() statement. No blog/article I have found until now include that line. I even reinstalled my glade and python thinking the error was with them.

Thank a looooooot!!!

Shrikant Sharat

Re: Rapid Application Development with Python and Glade

Anonymous's picture

What about signal names which includes '-'

Here's my solution
-------------------

"""GWidget - Making use of glade files easier.

:See:
GWidget
"""
import sys
import gtk
from gtk import glade
import gobject

class GWidget(object):
"""Wrapper for widgets in glade file.

Each toplevel widget in a glade file can be associated with a GWidget
class. Methods of that class if has prefix `on_`, are treated as
signal handlers for widgets under the tree of toplevel widget in
glade file. Signal handlers are of the form::
on_widgetname__signalname

``signalname`` if contains hyphens should be replaced with
underscores. If signal handler method name additionally ends with
double underscore (__), then ``connect_after`` is used to connect
that method.

``on_quit`` method if defined will be connected to ``delete-event``
event of the toplevel widget.

Any widget in the glade file (comming under toplevel widget) can be
accessed as if there is an instance variable named by that widget name.
Toplevel widget is always accessible through the ``widget`` instance
variable.

:CVariables:
- `GLADE_FILE`: Path of the glade file to use.
- `TOPLEVEL_NAME`: Name of the toplevel widget.
"""

# Path of glade file
GLADE_FILE = None
# Toplevel widget name
TOPLEVEL_NAME = None

# Properties
gladexml = property(lambda self: self._gladexml, doc='GladeXML object')
widget = property(lambda self: self._widget, doc='Toplevel widget')

def __init__(self, autoconnect_now=True):
"""
:Parameters:
- `autoconnect_now`: If False, doesn't autoconnect signals
"""
self._gladexml = glade.XML(self.GLADE_FILE, self.TOPLEVEL_NAME)
self._widget = self.gladexml.get_widget(self.TOPLEVEL_NAME)
if hasattr(self, 'on_quit'):
self._widget.connect('delete-event', self.on_quit)
self._widgets_cache = {} # Widgets are stored in cache for
# quicker future retrieval
if autoconnect_now:
self.autoconnect_signals()

def autoconnect_signals(self, prefix='on_'):
"""Autoconnect methods with unique format with widget actions.

See the class documentation string for more info.
"""
conn_func = gobject.GObject.connect
conn_after_func = gobject.GObject.connect_after
wid_in = len(prefix)
for key in self.__class__.__dict__:
hdl = getattr(self, key)
if callable(hdl) and key.startswith(prefix):
# Assumption: signal names doesn't contain double-underscores
if key.endswith('__'):
key = key[:-2]
connect_func = conn_after_func
else:
connect_func = conn_func
sig_in = key.rfind('__')
if sig_in >sys.stderr, 'Unknown callback method %s' % key
continue
wid_name = key[wid_in:sig_in]
sig_name = key[sig_in+2:]
sig_name.replace('_', '-')
new_widget = self._gladexml.get_widget(wid_name) or getattr(self, wid_name)
if new_widget is not None:
connect_func(new_widget, sig_name, hdl)
else:
print >>sys.stderr, 'Handler error for [%s]. No such widget [%s]' % (key, wid_name)

def __getattr__(self, attr):
try:
# Return the Gtk widget if cached
return self._widgets_cache[attr]
except KeyError:
# Return widget from gladexml
new_widget = self._gladexml.get_widget(attr)
if new_widget is not None:
self._widgets_cache[attr] = new_widget
return new_widget
# AttributeError
raise

GladeGenConfig.py file is not python

Anonymous's picture

The GladeGenConfig.py file is a gzip file according
to the "file" command.

When renamed with a gz suffix and unzipped the
resulting file is a tar file which contains...

-rwxrwxr-- jill/ljedit 10867 2004-04-21 10:27:26 GladeGen.py
-rwxrwxr-- jill/ljedit 4992 2004-04-21 10:27:26 GladeWindow.py
-rw-rw-r-- jill/ljedit 2438 2004-04-21 10:27:39 MathFlash-create.py
-rw-rw-r-- jill/ljedit 2170 2004-04-21 10:27:39 MathFlash-create-short-lines.py
-rw-rw-r-- jill/ljedit 25161 2004-04-21 10:27:52 mathflash.glade
-rw-rw-r-- jill/ljedit 279 2004-04-21 10:27:52 mathflash.gladep
-rwxrwxr-x jill/ljedit 4752 2004-04-21 10:27:39 MathFlash.py
-rwxrwxr-x jill/ljedit 4752 2004-04-21 10:27:39 MathFlash.py-latest

But still no GladeGenConfig.py file....

GladeGenConfig.py problem

Anonymous's picture

Appears to be a binary file in the .tgz

Re: GladeGenConfig.py problem

Anonymous's picture

It appears to have been fixed.
The author should have posted that here.

Webinar
One Click, Universal Protection: Implementing Centralized Security Policies on Linux Systems

As Linux continues to play an ever increasing role in corporate data centers and institutions, ensuring the integrity and protection of these systems must be a priority. With 60% of the world's websites and an increasing share of organization's mission-critical workloads running on Linux, failing to stop malware and other advanced threats on Linux can increasingly impact an organization's reputation and bottom line.

Learn More

Sponsored by Bit9

Webinar
Linux Backup and Recovery Webinar

Most companies incorporate backup procedures for critical data, which can be restored quickly if a loss occurs. However, fewer companies are prepared for catastrophic system failures, in which they lose all data, the entire operating system, applications, settings, patches and more, reducing their system(s) to “bare metal.” After all, before data can be restored to a system, there must be a system to restore it to.

In this one hour webinar, learn how to enhance your existing backup strategies for better disaster recovery preparedness using Storix System Backup Administrator (SBAdmin), a highly flexible bare-metal recovery solution for UNIX and Linux systems.

Learn More

Sponsored by Storix