Creating a GUI using PyGTK and Glade

Article by Mark Mruss, originally posted on www.learningpython.com

After spending some time creating a GUI using TKinter and having it be pretty easy, but getting frustrated by how linked my code and the GUI was, I decided to look into creating a GUI using another toolkit. After looking around at the options for a while I settled on using PyGTK and Glade

The reason I decided upon using these two technologies is because they are cross platform and using GLADE satisfies my wish to separate the code form the GUI.

If you've never heard of Glade before, it's "a User Interface Builder for GTK+ and GNOME". It generates XML files which describe the desired GUI.

I'll let the pyGTK website describe what PyGTK is:

PyGTK provides a convenient wrapper for the GTK+ library for use in Python programs, taking care of many of the boring details such as managing memory and type casting. When combined with PyORBit and gnome-python, it can be used to write full featured Gnome applications.

So there you go, I'm writing this on my freshly installed Debian system. If you are running Debian, or a Debian based distribution getting PyGTK and Glade is pretty simple:

apt-get install python-gtk python-glade2

Now lets create our first simple GUI, this is what you will be greeted with when you first start GLADE:

Python RSS Reader

What we need to do is press the "Window" button on the GLADE palette to create our base window. We can then edit the properties of the window in the Properties Window:

Python RSS Reader

We'll call our window MainWindow and we will set the title to be "Hello World… Again!".

If you are used to using an integrated GUI builder, for example Visual Studio, using glade might feel a bit strange the first few times. Especially since you don't actually place your controls anywhere you want on the screen, instead you "pack" them. Strangely enough (at least for me) it actually seems like this is the way the most GUI builders work and applications like Visual Studio are actually the odd ones.

Either way back to the tutorial, the first thing we are going to do is add a label to tell the user to click the button (of course we could just put this text on the button, but how much fun is only one widget?). Since GTK used containers to pack widgets the first thing that we need to do is add a container. We are going to place the label above the Button so we will use a Vertical Box with two rows. To add the vertical Box, simply click on it in the Glade Pallet, and then click on our main window. A small dialog will come up and ask you how many rows you want, we want two in this case.

The next thing we'll do is add the label by clicking on the label button in the GLADE balled and then click on the first row in the container that we just created. We'll keep the default name (lable1) but we will change the text to be: "Please click on the button!". Changing the text is done in the Glade Properties window, which if you have not noticed by now, displays, and allows you to edit, the properties of the currently selected widget.

The next thing we'll do is add a button in the same way that we added the Label widget except we will add it in the second row. We will call the button btnHelloWorld, and set it's label to be "Click me!"

We now need to set our project options, I'm going to call this project PyHelloWorld and save it in my projects/PyHelloWorld folder.

Python RSS Reader

So that's it, you'll see in the PyHelloWorld folder, that two files have been created, one is the glade project file and has the .gladep extension, and the other is our glade GUI XML file with the extension .glade.

Now we need to create a python program that will load the glade file and display it. So in the same folder I am going to create a file called PyHelloWorld.py.

PyGame Window

Now the first thing we are going to have to do is import all of the libraries that we need for our project:

#!/usr/bin/env python

import sys
try:
 	import pygtk
  	pygtk.require("2.0")
except:
  	pass
try:
	import gtk
  	import gtk.glade
except:
	sys.exit(1)

The next thing that we need to do is create our main class, I'm going to call it HellowWorldGTK. The we will use write the __init__ function to load our glade file:

class HellowWorldGTK:
	"""This is an Hello World GTK application"""

	def __init__(self):
		
		#Set the Glade file
		self.gladefile = "pyhelloworld.glade"  
	        self.wTree = gtk.glade.XML(self.gladefile) 
		
		#Get the Main Window, and connect the "destroy" event
		self.window = self.wTree.get_widget("MainWindow")
		if (self.window):
			self.window.connect("destroy", gtk.main_quit)

The first thing that we do (after defining the class) is specify the glade file that we are going to use and create a gtk.glade.XML object using our glade file. Here is a description of the object taken from the pyGTK2 reference:

This object represents an `instantiation' of an XML interface description. When one of these objects is created, the XML file is read, and the interface is created. The gtk.glade.XML object then provides an interface for accessing the widgets in the interface by the names assigned to them inside the XML description.

The gtk.glade.XML object can also be used to connect handlers to the named signals in the description. Libglade also provides an interface by which it can look up the signal handler names in the program's symbol table and automatically connect as many handlers up as it can that way.

So what we are doing when we create our gtk.glade.XML object is creating and loading our main interface.

The next thing that we go is get an instance to our main window and connect the "destroy" event with the get.main_quit() function. This basically quits our application when the main window is closed. Otherwise the application will continue to run when the main window is closed (which we obviously don't want).

That's it for our HelloWorldGTK class, the next thing that we need to do is create an instance of our class and then start the GTK main loop:

if __name__ == "__main__":
	hwg = HellowWorldGTK()
	gtk.main()

That's it, pretty easy so far, if you run this file you will be greeted with our little GTK window that doesn't do anything yet except quit properly when you close the window.

PyGame Window

The next step is to connect the button click event to a function. To do this we will need to use Glade again to edit our interface.

In the main window we need to select our button object and then in the properties Window select the "Signals" tab. There we will add a new signal by clicking on the signal browse button (…) and selecting "clicked". This will create a handler called "on_btnHelloWorld_clicked" by default. We could change the name of this handler if we wanted but for now the default is good enough.

PyGame Window

That's it for the work in Glade, now what we need to do is connect that event to something in our code. Fortunately this is pretty easily done using the gtk.glade.XML.signal_autoconnect function.

#Create our dictionay and connect it
dic = { "on_btnHelloWorld_clicked" : self.btnHelloWorld_clicked,
	"on_MainWindow_destroy" : gtk.main_quit }
self.wTree.signal_autoconnect(dic)

Basically the dictionary is created using the name of the event, and the function to connect it to. You can see that we connect our button's click event with a new function, and that we connect the "on_MainWindow_destroy" event with the gtk.mainquit() function, this basically is a replacement for our earlier code that quit the program when the window is closed. If you want to use that version of the dictionary you should go and add the destroy event to the main window in glade.

The next thing to do is create our btnHelloWorld_clicked function to the HellowWorldGTK class:

def btnHelloWorld_clicked(self, widget):
	print "Hello World!"

Pretty simple! Now when we run it, and click on the "Click Me!" button we see "Hello World!" written our to the command line.

That's it for this lesson, but so far I really like what I see working with PyGTK and Glade. Here is the full source:

#!/usr/bin/env python

import sys
try:
 	import pygtk
  	pygtk.require("2.0")
except:
  	pass
try:
	import gtk
  	import gtk.glade
except:
	sys.exit(1)

class HellowWorldGTK:
	"""This is an Hello World GTK application"""

	def __init__(self):
		
		#Set the Glade file
		self.gladefile = "pyhelloworld.glade"  
	        self.wTree = gtk.glade.XML(self.gladefile) 
		
		#Create our dictionay and connect it
		dic = { "on_btnHelloWorld_clicked" : self.btnHelloWorld_clicked,
			"on_MainWindow_destroy" : gtk.main_quit }
		self.wTree.signal_autoconnect(dic)

	def btnHelloWorld_clicked(self, widget):
		print "Hello World!"


if __name__ == "__main__":
	hwg = HellowWorldGTK()
	gtk.main()

Helpful links:

http://www.linuxjournal.com/article/6586
http://www.async.com.br/~kiko/pygtk-web/articles/bitpodder/BitPodder.htm
http://www.linuxjournal.com/article/7421
http://www.pygtk.org/articles.html
http://www.pygtk.org/tutorial.html