Writing pygtk applications with style, using pygtkhelpers
Introduction
pygtkhelpers is an awesome library for writing pygtk applications, it was developed by pida developers and makes the pygtk programming experience much better, let’s start with the tutorial.
I’ve used this library for my project, filesnake, I want to explain my workflow.
GUIs like template
In pygtkhelpers “glade” files and hand written GUI blends together in a wonderful manner, in particular each piece of the GUI is separate from the rest and give you a lot of control, flexibility and mantainablity.
I started with glade, writing a “skeleton gui”. It’s a window with a VBox inside (3 slots). I packed in each slot of the VBox a gtk.EventBox (also another gtk.VBox would have been fine for the purpose), these EventBoxes serves as “placeholders” for other pieces of the GUI, the menu, the userlist and the status bar.
I’ve written the very little code to make the GUI run:
class FileSnakeGUI(WindowView):
builder_file = "main_win.glade"
def test_run():
fs = FileSnakeGUI()
fs.show_and_run()
if __name__ == '__main__':
test_run()
It’s time to write the three components:
- menu
- userlist
- statusbar
The Menu
I’ve written another glade file that contains a window with the menu inside, the library will handle the extraction of the menu from the “container” window for you.
To connect the events I’ve used the signal handling facilities provided by pygtkhelpers.
The connection is made automatically with the naming convention on_widget__signal, this simple convention let us to eliminate the boilerplate code related to the “connect” methods.
from pygtkhelpers.delegates import SlaveView
class Menu(SlaveView):
builder_file = "menu.glade"
def __init__(self, parent):
SlaveView.__init__(self)
self.parent = parent
def on_quit__activate(self,*a):
self.parent.hide_and_quit()
def on_sendfile__activate(self, *a):
self.parent.send_file()
Handwritten GUIs
I’ve written the statusbar and the userlist by hand, The code can be as complex as you want, demostrating the flexibility and “invisibility” of the framework, all the code is well packed and organized on its own place.
When subclassing a generic Slave/WindowView (subclasses of BaseDelegate) you can override this methods to customize the behaviour of the class :
- create_ui: in this method you can manually write your GUI, usually you put there all the “add_slave” code
- on_mywidget__event: these are the signal handlers, facilities that let
you write cleaner code without all the self.connect stuff - __init__: you can pass custom initializer, and various control
code not related to the gui code (nothing stops you to do that in
the create_ui, it’s just to add a bit of conventions)
Here’s the UserList code, you can add additional methods to simplify
external access, like add_user(). This “additional method” would be
used i.e. in the main controller (FileSnakeGUI). It’s a component, and it’s reusable.
# Defining a user container
User = namedtuple("User", "name icon address port")
# UserList section
class UserList(SlaveView):
def create_ui(self):
model = gtk.ListStore(object)
treeview = gtk.TreeView(model)
treeview.set_name("User List")
iconrend = gtk.CellRendererPixbuf()
inforend = gtk.CellRendererText()
iconcol = gtk.TreeViewColumn('Icon', iconrend)
infocol = gtk.TreeViewColumn('Info', inforend)
iconcol.set_cell_data_func(iconrend, self._icon_data)
infocol.set_cell_data_func(inforend, self._info_data)
treeview.append_column(iconcol)
treeview.append_column(infocol)
treeview.set_headers_visible(False)
self.store = model
self.treeview = treeview
self.widget.add(treeview)
def _icon_data(self, column, cell, model, iter):
row = model[iter]
user = row[0]
cell.set_property("pixbuf",gtk.gdk.pixbuf_new_from_file(user.icon))
def _info_data(self, column, cell, model, iter):
row = model[iter]
user = row[0]
template = "<big><b>{user}</b></big>\n<small><i>{address}:{port}</i></small>"
label = template.format(user=user.name,
address=user.address,
port=user.port)
cell.set_property("markup",label)
def add_user(self, user):
self.store.append([user])
The statusbar doesn’t introduce anything new. You can find the source following the link at the end of the article.
To pack together the gui, we’ll use the add_slave method, it adds to a container widget (the EventBoxes we placed) the widget defined in the slave view, the resulting code is that:
class FileSnakeGUI(WindowView):
builder_file = "main_win.glade"
def create_ui(self):
self.userlist = UserList()
self.add_slave("statusbar_cont", Statusbar())
self.add_slave("menu_cont", Menu(self))
self.add_slave("userlist_cont", self.userlist)
def on_window1__delete_event(self, *a):
self.hide_and_quit()
pygtk signals facilities, now you haven’t any excuse
It’s trivial to add your own signals to a BaseDelegate instance, let’s see
how to add a “user-added” signal to my UserList:
from pygtkhelpers.utils import gsignal
class UserList(SlaveView):
gsignal("user-added",object)
# ... source code defined before...
def add_user(self, user):
self.emit("user-added", user)
self.store.append([user])
It’s just one line of code and you can connect it like a gtk widget,
implementing in the easiest way I’ve seen the observer pattern. It’s
cool!
Refactoring the UserList with pygtkhelpers.ui.objectlist.ObjectList widget
One of the most useful feature of pygtkhelpers are ObjectList and ObjectTree, pythonic versions of the common gtk list/tree widgets. They automate the tedious task of setting up treeview widgets using a very powerful (yet customizable) manner.
In a few words, given an object, his attributes can be mapped to the treeview through Columns that almost automatically select the correct renderer for the data to be displayed, like in the following scheme:
The resulting code, is much cleaner and readable:
from pygtkhelpers.ui.objectlist import Column,ObjectList
from pygtkhelpers.delegates import SlaveView
import gtk
from collections import namedtuple
# This is the "user object"
User = namedtuple("User", "name address port icon")
class UserList(SlaveView):
def create_ui(self):
# Columns are intended to map UserEntry object, with this signature: Column("attribute", type)
self.users = ObjectList(columns = [Column("icon",gtk.gdk.Pixbuf),
Column("info",str, use_markup=True)])
self.users.set_headers_visible(False)
self.widget.add(self.users)
def add_user(self, user):
self.users.append(UserEntry(user))
# The UserEntry is mapped by the treeview with the attributes info and icon
class UserEntry(object):
def __init__(self, user):
template = "<big><b>{user}</b></big>\n<small><i>{address}:{port}</i></small>"
self.info = template.format(user=user.name,
address=user.address,
port=user.port)
self.icon = gtk.gdk.pixbuf_new_from_file(user.icon)
There are also other interesting features that really boost the pygk GUI programming expecially regarding ObjectList, but also with the pygtkhelpers.ui.dialogs module: well cooked dialogs for common uses.
You can find the full sourcecode at the blog repo:
http://bitbucket.org/gabriele/pygabriel-blogging/src/tip/pygtkhelpers/source/


it would be really nice to show the pygtkhelpers.ui.objectlist use instead of normal treeviews
Thank you very much for commenting! I’ve used an hand written treeview just to show some sort of hand-written code. Anyway, I’ll take up your hints and update the blog post with a “refactoring” using objectlist. Bye!
Glad you like PyGTKHelpers, did you consider using the ObjectList for your userlist? Would have saved a bit more code there.