22. Glade and Gtk.Builder

The Gtk.Builder class offers you the opportunity to design user interfaces without writing a single line of code. This is achieved by defining the interface in an XML file and then loading that XML UI definition at runtime using the Builder class which creates the objects automatically. To avoid writing the XML manually use the Glade application which lets you create the user interface in a WYSIWYG (what you see is what you get) manner

This method has several advantages:

  • Less code needs to be written.

  • UI changes can be seen more quickly, so UIs are able to improve.

  • Designers without programming skills can create and edit UIs.

  • The description of the user interface is independent from the programming language being used.

There is still code required for handling interface changes triggered by the user, but Gtk.Builder allows you to focus on implementing that functionality.

22.1. Creating and loading the .glade file

First of all you have to download and install Glade. There are several tutorials about Glade, so this is not explained here in detail. Let’s start by creating a window with a button in it and saving it to a file named example.glade. The resulting XML file should look like this.

<?xml version="1.0" encoding="UTF-8"?>
<interface>
  <!-- interface-requires gtk+ 3.0 -->
  <object class="GtkWindow" id="window1">
    <property name="can_focus">False</property>
    <child>
      <object class="GtkButton" id="button1">
        <property name="label" translatable="yes">button</property>
        <property name="use-action-appearance">False</property>
        <property name="visible">True</property>
        <property name="can-focus">True</property>
        <property name="receives-default">True</property>
      </object>
    </child>
  </object>
</interface>

To load this file in Python we need a Gtk.Builder object.

builder = Gtk.Builder()
builder.add_from_file("example.glade")

The second line loads all objects defined in example.glade into the Builder object.

It is also possible to load only some of the objects. The following line would add only the objects (and their child objects) given in the tuple.

# we don't really have two buttons here, this is just an example
builder.add_objects_from_file("example.glade", ("button1", "button2"))

These two methods exist also for loading from a string rather than a file. Their corresponding names are Gtk.Builder.add_from_string() and Gtk.Builder.add_objects_from_string() and they simply take a XML string instead of a file name.

22.2. Accessing widgets

Now that the window and the button are loaded we also want to show them. Therefore the Gtk.Window.show_all() method has to be called on the window. But how do we access the associated object?

window = builder.get_object("window1")
window.show_all()

Every widget can be retrieved from the builder by the Gtk.Builder.get_object() method and the widget’s id. It is really that simple.

It is also possible to get a list of all objects with

builder.get_objects()

22.3. Connecting Signals

Glade also makes it possible to define signals which you can connect to handlers in your code without extracting every object from the builder and connecting to the signals manually. The first thing to do is to declare the signal names in Glade. For this example we will act when the window is closed and when the button was pressed, so we give the name “onDestroy” to the callback handling the “destroy” signal of the window and “onButtonPressed” to the callback handling the “pressed” signal of the button. Now the XML file should look like this.

<?xml version="1.0" encoding="UTF-8"?>
<interface>
  <!-- interface-requires gtk+ 3.0 -->
  <object class="GtkWindow" id="window1">
    <property name="can-focus">False</property>
    <signal name="destroy" handler="onDestroy" swapped="no"/>
    <child>
      <object class="GtkButton" id="button1">
        <property name="label" translatable="yes">button</property>
        <property name="use-action-appearance">False</property>
        <property name="visible">True</property>
        <property name="can-focus">True</property>
        <property name="receives-default">True</property>
        <property name="use-action-appearance">False</property>
        <signal name="pressed" handler="onButtonPressed" swapped="no"/>
      </object>
    </child>
  </object>
</interface>

Now we have to define the handler functions in our code. The onDestroy should simply result in a call to Gtk.main_quit(). When the button is pressed we would like to print the string “Hello World!”, so we define the handler as follows

def hello(button):
    print("Hello World!")

Next, we have to connect the signals and the handler functions. The easiest way to do this is to define a dict with a mapping from the names to the handlers and then pass it to the Gtk.Builder.connect_signals() method.

handlers = {
    "onDestroy": Gtk.main_quit,
    "onButtonPressed": hello
}
builder.connect_signals(handlers)

An alternative approach is to create a class which has methods that are called like the signals. In our example the last code snippet could be rewritten as:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
from gi.repository import Gtk


class Handler:
    def onDestroy(self, *args):
        Gtk.main_quit()

    def onButtonPressed(self, button):
        print("Hello World!")
builder.connect_signals(Handler())

22.4. Example

The final code of the example

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import gi

gi.require_version("Gtk", "3.0")
from gi.repository import Gtk


class Handler:
    def onDestroy(self, *args):
        Gtk.main_quit()

    def onButtonPressed(self, button):
        print("Hello World!")


builder = Gtk.Builder()
builder.add_from_file("builder_example.glade")
builder.connect_signals(Handler())

window = builder.get_object("window1")
window.show_all()

Gtk.main()

22.5. Gtk.Template

Gtk.WidgetClass allows UI definition files to be used to extend a widget, PyGObject provides Gtk.Template as a way of accessing this from Python.

The UI definition file used in the example needs a small change to include a <template> element:

<?xml version="1.0" encoding="UTF-8"?>
<interface>
  <!-- interface-requires gtk+ 3.0 -->
  <template class="window1" parent="GtkWindow">
    <signal name="destroy" handler="onDestroy" swapped="no"/>
    <child>
      <object class="GtkButton" id="button1">
        <property name="label" translatable="yes">button</property>
        <property name="use-action-appearance">False</property>
        <property name="visible">True</property>
        <property name="can-focus">True</property>
        <property name="receives-default">True</property>
        <property name="use-action-appearance">False</property>
        <signal name="pressed" handler="onButtonPressed" swapped="no"/>
      </object>
    </child>
  </template>
</interface>

Then it can be used to implement the example with a Gtk.Window subclass:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import gi

gi.require_version("Gtk", "3.0")
from gi.repository import Gtk


@Gtk.Template(filename="template_example.ui")
class Window1(Gtk.Window):
    __gtype_name__ = "window1"

    @Gtk.Template.Callback()
    def onDestroy(self, *args):
        Gtk.main_quit()

    @Gtk.Template.Callback()
    def onButtonPressed(self, button):
        print("Hello World!")


window = Window1()
window.show()

Gtk.main()

More information can be found at the PyGObject website.