GTK3 Composite Widget Templates for Python

Recently, the other developers of the Exaile audio player and I decided to finally migrate to GTK3 and GStreamer 1.x. I mentioned that I wanted to use some code I had developed a few years ago to get rid of the manual UI building code that we had and replace it with GtkBuilder XML files, and @mathbr noted that GTK had added a new feature called ‘composite widget templates’ a few years ago. The ideas were similar to mine, and reading the comments in a blog post about the Vala implementation inspired me to create a working version of this for Python. The first implementation took a few hours, and I’ve been adding improvements ever since as I’ve been integrating this into Exaile.

Here’s how the vala demo code from that blog post looks like in Python, turns out it’s not *that* different:

from __future__ import print_function
from gi.repository import Gtk
from gi_composites import GtkTemplate

@GtkTemplate(ui='mywidget.ui')
class MyWidget(Gtk.Box):

    __gtype_name__ = 'MyWidget'
    
    entry = GtkTemplate.Child()

    def __init__(self, text):
        super(Gtk.Box, self).__init__()
        self.init_template()
        self.entry.set_text(text)
    
    @GtkTemplate.Callback
    def button_clicked(self, widget):
        print("The button was clicked with entry text: %s" % self.entry.get_text())

    @GtkTemplate.Callback
    def entry_changed(self, widget):
        print("The entry text changed: %s" % self.entry.get_text())

The key pieces to note are:

  • Use the @GtkTemplate decorator to load the template for your widget
  • Use GtkTemplate.Child to create attributes on your widget that will be loaded from the XML file (there’s also GtkTemplate.Child.widgets(n) if you need to declare multiple widgets)
  • Use @GtkTemplate.Callback decorator to mark methods to be connected to signals as declared in the XML file

For the full demo + associated GtkBuilder XML file check out the github repo.

I’d love to see this functionality included with GTK’s python bindings, and in fact after creating this I found a bug open on the GNOME bugzilla with a patch to PyGObject to allow python users to use it, but for whatever reason it never got merged. My implementation works on the current release of PyGObject, and possibly older versions too.

Want to get rid of boilerplate in your GTK3 python application? Check out the examples/code on github.

Leave a Reply