Source

pygabriel-blogging / pygtkhelpers / article.html

Full commit
<h2>Writing pygtk application with style, using pygtkhelpers</h2>

 <a href="http://packages.python.org/pygtkhelpers/index.html">pygtkhelpers</a>
is an awesome library for writing pygtk applications, it was developed
by <a href="http://pida.co.uk/pida">pida</a> developers and makes the
pygtk programming experience much better, let's start with the
tutorial.

 I've used this library for my
project, <a href="http://bitbucket.org/gabriele/filesnake/wiki/Home">filesnake</a>,
I want to explain my workflow.

<h3> GUIs like template </h3>
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 and in each part of the vbox I filled with 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.

<img src="pictures/filesnake-skeleton.png" class="" alt="" />

I've written the very little code to make the GUI
run:

[sourcecode=python]
from pygtkhelpers.delegates import WindowView

class FileSnakeGUI(WindowView):
    builder_file = "main_win.glade"

f test_run():
    fs = FileSnakeGUI()
    fs.show_and_run()

if __name__ == '__main__':
    test_run()

[/sourcecode]

It's time to write the three components:
<ul>
  <li>menu</li>
  <li>userlist</li>
  <li>statusbar</li>
</ul>

Starting from the menu, I've written another glade file with info
about menu, and I've used the signal handling throught the stuff.

Don't worry about placing only the menu in the glade file, the library
handle the finding of the container window and unpacks the widget from
it.

The connection is made automatically with the naming convention on_widget__signal.

[sourcecode]
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()
[/sourcecode]

<h3>Handwritten GUIs</h3>

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.

A generic Slave/WindowView (subclasses of BaseDelegate) has this methods:

<ul>
  <li>create_ui: in this method you can manually write your GUI,
    usually you put there all the "add_slave" code</li>
  <li>
    on_mywidget__event: these are the signal handlers, facilities that let
    you write cleaner code without all the self.connect stuff</li>
  <li>__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)</li>
</ul>

Here's the UserList code, you can add additional methods to simplify
external access, like add_user(). This "additional method" would be
used in the main controller (FileSnakeGUI), for example. It's a component, and it's reusable.

[sourcecode]
# 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])

[/sourcecode]

The statusbar doesn't introduce anything new. You can find the source
appended.

<h3>pygtk signals facilities, now you haven't any excuse</h3>

It's trivial to add your own signals to your BaseDelegate, let's see
how to add a "user-added" signal to my UserList:

[sourcecode]
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])

[/sourcecode]


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!

 Other sweetness are pygtkhelpers.dialogs, premade dialogs for a lot
of stuff, like yesno, open dialog etc...  There are also new widgets
very useful for conventional liststore/treeview programming. Writing
an editable treeview is no more a pain.

You can find the full sourcecode on the blog repo: 

http://bitbucket.org/gabriele/pygabriel-blogging/pygtkhelpers