Skip to main content

ListStore

Store data sets in ListStores and use ComboBox and TreeView to display the data

There are a bunch of elements required to store and display tables of data in GTK+ applications:

  1. The model to administrate data. There are two types:
    • ListStore: flat table, besides string and numerical values the rows also can be of GTK+ element type (like buttons or checkboxes), input of data sets in Glade possible

    • TreeStore: just like ListStore but rows can possess child rows, input of data sets in Glade is not possible (see also TreeStore article)

  2. Widgets:
    • TreeView: show, sort and edit data; used by both store model types; a data store can be used my multiple TreeView widgets

    • ComboBox: comboboxes are used to limit input to given list items, this list can be stored in a List/TreeStore (see also spinbutton and combobox article)

    • CellRenderers: Subwidgets to specify source, layout and other properties (like being editable) of displayed data rows

/images/09_treestore2.thumbnail.png

Glade

ListStore

In the example there is one ListStore created via "Miscellaneous > List Store" which will be later used by three Widgets.

First there are some rows created. ListStore data sets can be inserted in Glade but this in practise is only convenient for typing in few data sets.

Content changes in the ListStore are simultaneously updated in the Widgets using the ListStore. For individual sorting of the same List/TreeStore it is needed to create Gtk.TreeModelSort elements (this element is used in the example of the TreeStore article).

/images/09_treestore1.thumbnail.png

Widgets

ComboBox

Creating the widget you are prompted to choose as "TreeView Model". In the edit mode accessible via "Edit > Hierarchy" there is created a CellRendererText. In the first field ("Text") the column to load the items of the dropdown menu from is set. To process the selection you will need the changed signal.

TreeView #1

The first TreeView widget is placed within a Gtk.ScrolledWindow container. Like in a ComboBox there are created CellRenderers representing a column to show in the TreeView table. If the sort indicator is activated columns can be sorted on a column table click. Columns do not have to be sorted according to the columns they show.

TreeView #2

The second TreeView widget is created within a Gtk.ViewPort. This container widget does not provide scroll bars but the automatically adapts the necessary size to display the whole content. So for larger tables you will need the Gtk.ScrolledWindow. The sort indicator is deactivated and the middle column ("Description") is made editible with the signal "edited" allocated.

Button

The button's function is appending a row to the ListStore, so the clicked signal is required.

Python

TreeStore

The ListStore's row can be iterated over via for row in store. New rows are added by append, other options are insert or remove to add or delete rows at specific positions.

ComboBox

For accessing a data row you need a Gtk.TreeIter object which points to the position in the model (this can also be achieved by a Gtk.TreePath object).

iter,model = widget.get_active_iter(),widget.get_model()
row = model[iter]
print("Selection:",row[0])

Edit cells

The edited signal passes the position and content of the edited cell. The new content of the CellRendererText has to explicitly be committed to the data store otherwise the content will return to the pre edit state. This can be accomplished by using the passed TreePath position.

def on_cellrenderer_descr_edited(self,widget,pos,edit):
    x.store[int(pos)][1] = edit

Listings

Glade

09_liststore.glade (Source)

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
  <requires lib="gtk+" version="3.20"/>
  <object class="GtkListStore" id="liststore">
    <columns>
      <!-- column-name name -->
      <column type="gchararray"/>
      <!-- column-name descr -->
      <column type="gchararray"/>
      <!-- column-name num -->
      <column type="gint"/>
    </columns>
    <data>
      <row>
        <col id="0" translatable="yes">one</col>
        <col id="1" translatable="yes">textextext</col>
        <col id="2">12345</col>
      </row>
      <row>
        <col id="0" translatable="yes">two</col>
        <col id="1" translatable="yes">bla blubb</col>
        <col id="2">479</col>
      </row>
      <row>
        <col id="0" translatable="yes">three</col>
        <col id="1" translatable="yes"></col>
        <col id="2">0</col>
      </row>
    </data>
  </object>
  <object class="GtkWindow" id="window">
    <property name="width_request">300</property>
    <property name="can_focus">False</property>
    <signal name="destroy" handler="on_window_destroy" swapped="no"/>
    <child>
      <object class="GtkBox">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="orientation">vertical</property>
        <child>
          <object class="GtkComboBox" id="cbox">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="model">liststore</property>
            <property name="entry_text_column">0</property>
            <signal name="changed" handler="on_cbox_changed" swapped="no"/>
            <child>
              <object class="GtkCellRendererText"/>
              <attributes>
                <attribute name="text">0</attribute>
              </attributes>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkBox">
            <property name="width_request">150</property>
            <property name="height_request">250</property>
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="orientation">vertical</property>
            <child>
              <object class="GtkScrolledWindow">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="shadow_type">in</property>
                <child>
                  <object class="GtkTreeView">
                    <property name="visible">True</property>
                    <property name="can_focus">True</property>
                    <property name="model">liststore</property>
                    <property name="headers_clickable">False</property>
                    <child internal-child="selection">
                      <object class="GtkTreeSelection"/>
                    </child>
                    <child>
                      <object class="GtkTreeViewColumn">
                        <property name="title" translatable="yes">Name</property>
                        <property name="sort_indicator">True</property>
                        <property name="sort_column_id">0</property>
                        <child>
                          <object class="GtkCellRendererText"/>
                          <attributes>
                            <attribute name="text">0</attribute>
                          </attributes>
                        </child>
                      </object>
                    </child>
                    <child>
                      <object class="GtkTreeViewColumn">
                        <property name="title" translatable="yes">Description</property>
                        <property name="sort_indicator">True</property>
                        <property name="sort_column_id">2</property>
                        <child>
                          <object class="GtkCellRendererText"/>
                          <attributes>
                            <attribute name="text">1</attribute>
                          </attributes>
                        </child>
                      </object>
                    </child>
                    <child>
                      <object class="GtkTreeViewColumn">
                        <property name="title" translatable="yes">Number</property>
                        <property name="sort_indicator">True</property>
                        <property name="sort_column_id">2</property>
                        <child>
                          <object class="GtkCellRendererText"/>
                          <attributes>
                            <attribute name="text">2</attribute>
                          </attributes>
                        </child>
                      </object>
                    </child>
                  </object>
                </child>
              </object>
              <packing>
                <property name="expand">True</property>
                <property name="fill">True</property>
                <property name="position">0</property>
              </packing>
            </child>
            <child>
              <object class="GtkViewport">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <child>
                  <object class="GtkTreeView">
                    <property name="visible">True</property>
                    <property name="can_focus">True</property>
                    <property name="model">liststore</property>
                    <child internal-child="selection">
                      <object class="GtkTreeSelection"/>
                    </child>
                    <child>
                      <object class="GtkTreeViewColumn">
                        <property name="title" translatable="yes">Name</property>
                        <child>
                          <object class="GtkCellRendererText"/>
                          <attributes>
                            <attribute name="text">0</attribute>
                          </attributes>
                        </child>
                      </object>
                    </child>
                    <child>
                      <object class="GtkTreeViewColumn">
                        <property name="title" translatable="yes">Description</property>
                        <child>
                          <object class="GtkCellRendererText" id="cellrenderer_descr">
                            <property name="editable">True</property>
                            <signal name="edited" handler="on_cellrenderer_descr_edited" swapped="no"/>
                          </object>
                          <attributes>
                            <attribute name="text">1</attribute>
                          </attributes>
                        </child>
                      </object>
                    </child>
                    <child>
                      <object class="GtkTreeViewColumn">
                        <property name="title" translatable="yes">Number</property>
                        <child>
                          <object class="GtkCellRendererText"/>
                          <attributes>
                            <attribute name="text">2</attribute>
                          </attributes>
                        </child>
                      </object>
                    </child>
                  </object>
                </child>
              </object>
              <packing>
                <property name="expand">True</property>
                <property name="fill">True</property>
                <property name="position">1</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="expand">True</property>
            <property name="fill">True</property>
            <property name="position">1</property>
          </packing>
        </child>
        <child>
          <object class="GtkButton" id="add_row_button">
            <property name="label">gtk-add</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="receives_default">True</property>
            <property name="use_stock">True</property>
            <property name="always_show_image">True</property>
            <signal name="clicked" handler="on_add_row_button_clicked" swapped="no"/>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">2</property>
          </packing>
        </child>
      </object>
    </child>
    <child type="titlebar">
      <placeholder/>
    </child>
  </object>
</interface>

Python

09_liststore.py (Source)

#!/usr/bin/python
# -*- coding: utf-8 -*-

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


class Handler:

    def on_window_destroy(self, *args):
        Gtk.main_quit()

    def on_cbox_changed(self, widget):
        iter, model = widget.get_active_iter(), widget.get_model()
        row = model[iter]
        print("Selection:", row[0])

    def on_cellrenderer_descr_edited(self, widget, pos, edit):
        x.store[int(pos)][1] = edit

    def on_add_row_button_clicked(self,widget):
        x.store.append(list(x.more_rows[len(x.store) - 3]))
        #set button inactive when all rows are appended
        if len(x.store) == 7:
            x.button.set_sensitive(False)


class Example:

    def __init__(self):

        self.builder = Gtk.Builder()
        self.builder.add_from_file("09_liststore.glade")
        self.builder.connect_signals(Handler())

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

        self.button = self.builder.get_object("add_row_button")
        self.store = self.builder.get_object("liststore")

        #print all values
        [print(row[:]) for row in self.store]

        self.more_rows = [("four", "", 5739),
                          ("five", "", 120),
                          ("six", "", 4),
                          ("seven", "lucky number", 7),
                          ]

    def main(self):
        Gtk.main()


x = Example()
x.main()

Spoilt for choice

Spinbutton and Combobox

Named widgets simplify input of values by providing a value list or range. Value input is normally done via mouseclicks, key input is optional.

Glade

/images/08_combospin.thumbnail.png

Spinbutton

In Glade some properties of spinbutton widgets can be set like the minimum/maximum/default value. It is required to tie a adjustment widget to the spinbutton. To do so select "General > Spin Button Attributes > Adjustment" and assign or create an adjustment.

Looking at the example file the two spinbuttons represent month and year dates. The month spinbutton is set to be circular, the minimum value follows after passing the maximum value. This act emits the wrapped signal which is assigned to then adjust the year date of the second spinbutton.

Combobox

There are two different combobox widgets in GTK+:

GtkComboboxText

The list items of the dropdown menu are strings directly that are directly set in the Glade widget properties

GtkCombobox

The list items are stored in a ListStore or TreeStore element that can hold data sets instead of a plain list. These data stores are accessible to different widgets (see the "List article").

Both widgets can include an optional entry widget which has to be activated in "General > Has Entry". By activating an internal GtkEntry widget is created. It is important to set "Common > Widget Flags > Can focus".

The first ComboboxText widget in the exampe does not possess an entry field, the user can only select an item from the list. In both widgets the changed signal is assigned to retrieve the selection.

Python

Spinbutton

The value of a spinbutton can be easily retrieved and set via get_value and set_value.

In the example the values for the month and year date are set with the current dates. The on_spin_m_wrapped function changes the year date up or down according to the value set in month date.

Combobox

Combobox lists can be extended by using the append function, p.e.

[self.builder.get_object("comboboxtext2").append(None,entry) for entry in ("foo","bar","yes","no")]

You get the selected item by calling widget.set_active_text() which also passes the text of the optional text entry widget.

Listings

Glade

08_combospin.glade (Source)

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
  <requires lib="gtk+" version="3.20"/>
  <object class="GtkAdjustment" id="adj_j">
    <property name="lower">1234</property>
    <property name="upper">3000</property>
    <property name="step_increment">1</property>
    <property name="page_increment">10</property>
  </object>
  <object class="GtkAdjustment" id="adj_m">
    <property name="lower">1</property>
    <property name="upper">12</property>
    <property name="step_increment">1</property>
    <property name="page_increment">10</property>
  </object>
  <object class="GtkWindow" id="window">
    <property name="can_focus">False</property>
    <property name="default_width">400</property>
    <signal name="destroy" handler="on_window_destroy" swapped="no"/>
    <child>
      <object class="GtkBox">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="orientation">vertical</property>
        <child>
          <object class="GtkBox">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="homogeneous">True</property>
            <child>
              <object class="GtkSpinButton" id="spin_m">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="placeholder_text" translatable="yes">Monat</property>
                <property name="adjustment">adj_m</property>
                <property name="wrap">True</property>
                <signal name="wrapped" handler="on_spin_m_wrapped" swapped="no"/>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">1</property>
              </packing>
            </child>
            <child>
              <object class="GtkSpinButton" id="spin_y">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="adjustment">adj_j</property>
                <property name="climb_rate">1</property>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">2</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkComboBoxText" id="comboboxtext1">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <items>
              <item id="&lt;Geben Sie die Kennung ein&gt;" translatable="yes">eins</item>
              <item translatable="yes">zwei</item>
              <item translatable="yes">12345</item>
              <item translatable="yes">mehr Listenblabla</item>
            </items>
            <signal name="changed" handler="on_comboboxtext1_changed" swapped="no"/>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">2</property>
          </packing>
        </child>
        <child>
          <object class="GtkComboBoxText" id="comboboxtext2">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="has_entry">True</property>
            <signal name="changed" handler="on_comboboxtext2_changed" swapped="no"/>
            <child internal-child="entry">
              <object class="GtkEntry" id="entry">
                <property name="can_focus">True</property>
              </object>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">3</property>
          </packing>
        </child>
      </object>
    </child>
    <child type="titlebar">
      <placeholder/>
    </child>
  </object>
</interface>

Python

08_combospin.py (Source)

#!/usr/bin/python
# -*- coding: utf-8 -*-

import datetime

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


class Handler:

    def on_window_destroy(self, *args):
        Gtk.main_quit()

    def on_spin_m_wrapped(self, widget):
        if widget.get_value() == 1:
            x.spin_y.set_value(x.spin_y.get_value() + 1)
        else:
            x.spin_y.set_value(x.spin_y.get_value() - 1)

    def on_comboboxtext1_changed(self, widget):
        print("Auswahl ComboBox 1:", widget.get_active_text())

    def on_comboboxtext2_changed(self, widget):
        print("Auswahl ComboBox 2:", widget.get_active_text())


class Example:

    def __init__(self):

        self.builder = Gtk.Builder()
        self.builder.add_from_file("08_combospin.glade")
        self.builder.connect_signals(Handler())

        #set current values for month/year
        self.builder.get_object("spin_m").set_value(datetime.datetime.now().month)
        self.spin_y = self.builder.get_object("spin_y")
        self.spin_y.set_value(datetime.datetime.now().year)

        #set combobox list values
        [self.builder.get_object("comboboxtext2").append(None,entry) for entry in ("bla", "blubb", "ja", "nein")]

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

    def main(self):
        Gtk.main()


x = Example()
x.main()

Jumping off the greyscale

Pimp GUI using Cascading Style Sheets

CSS

GTK+ objects' layouts can be altered by using CSS. In my humble opinion the usage should not be exaggerated and the main part of the layout should be left to be managed by the theme. Links:

Glade

/images/07_css.thumbnail.png

CSS layout instructions are set in the source code. This example shows the usage of CSS in levelbars so 4 of them are created with Glade.

Python

CSS

The pattern for layout instructions is

widget [element] {
  font...
  color...
  background...
  ...
}

that are stored in a string variable loaded by the Gtk.CssProvider() class.

Levelbar

Levelbar values can be displayed in different colours depending on the defined value range (see article "Bars"), p.e. to visualize a critical battery status. The predefined offset markers are:

  • low (<=.25)

  • high (<=.75)

  • full (bis 1)

The values can be retrieved by the get_offset_value function or created/changed with the add_offset_value function.

In the example the 4th levelbar has an additional offset marker between high and full that's why a value of 0.8 is not visualized as full like it is in the 3rd levelbar.

self.bar.add_offset_value("alert",.9)

Listings

Glade

07_css.glade (Source)

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
  <requires lib="gtk+" version="3.20"/>
  <object class="GtkWindow" id="window">
    <property name="can_focus">False</property>
    <property name="default_width">400</property>
    <property name="default_height">150</property>
    <signal name="destroy" handler="on_window_destroy" swapped="no"/>
    <child>
      <object class="GtkBox">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="orientation">vertical</property>
        <property name="homogeneous">True</property>
        <child>
          <object class="GtkLevelBar" id="lev1">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="margin_top">5</property>
            <property name="margin_bottom">5</property>
            <property name="value">0.20000000000000001</property>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkLevelBar" id="lev2">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="margin_top">5</property>
            <property name="margin_bottom">5</property>
            <property name="value">0.5</property>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">1</property>
          </packing>
        </child>
        <child>
          <object class="GtkLevelBar" id="lev3">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="margin_top">5</property>
            <property name="margin_bottom">5</property>
            <property name="value">0.80000000000000004</property>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">2</property>
          </packing>
        </child>
        <child>
          <object class="GtkLevelBar" id="lev4">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="margin_top">5</property>
            <property name="margin_bottom">5</property>
            <property name="value">0.80000000000000004</property>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">3</property>
          </packing>
        </child>
      </object>
    </child>
    <child>
      <placeholder/>
    </child>
  </object>
</interface>

Python

07_css.py (Source)

#!/usr/bin/python
# -*- coding: utf-8 -*-

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


class Handler:

    def on_window_destroy(self, *args):
        Gtk.main_quit()


class Example:

    def __init__(self):

        self.builder = Gtk.Builder()
        self.builder.add_from_file("07_css.glade")
        self.builder.connect_signals(Handler())

        css = b"""

levelbar trough block.filled.low {
    background-color: green;
}

levelbar trough block.filled.high {
    background-color: yellow;
}

levelbar trough block.filled.alert {
    background-color: orange;
}

levelbar trough block.filled.full {
    background-color: red;
}
"""
        #load css stylesheet
        style_provider = Gtk.CssProvider()
        style_provider.load_from_data(css)

        Gtk.StyleContext.add_provider_for_screen(
            Gdk.Screen.get_default(),
            style_provider,
            Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
        )

        self.bar = self.builder.get_object("lev4")
        self.bar.add_offset_value("alert", .9)

        print("low:  ", self.bar.get_offset_value("low"))
        print("high: ", self.bar.get_offset_value("high"))
        print("alert:", self.bar.get_offset_value("alert"))
        print("full: ", self.bar.get_offset_value("full"))

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

    def main(self):
        Gtk.main()


x = Example()
x.main()

Bars

Progressbars and levelbars

Glade

/images/06_progresslevel.thumbnail.png

Progressbar

Progressbars usually show the current state of a longer lasting process. The widget offers two operation modes:

  1. proportional mode, the progress is expressed by a value between 0 and 1

  2. activity mode, a block moves back and forth

The widget posseses an optional text field. It shows the progress in percent if the field content is not specified otherwise.

Levelbar

Levelbar widgets are used as level indicators. The level is visualized like in a progressbar but it has some more properties:

  1. Two operation modes:

    1. continous: one single block represents the value

    2. discrete: the levelbar is split into a defined number of blocks, each block represents a value range

  2. Define minimum/maximum value, default is 0/1; the default number of blocks in discrete mode corresponds to the maximum value

  3. Change colours when exceeding predefined values (see also CSS article)

Python

Progressbar

In the example the first progressbar operates in proportional mode, the second in activity mode. The latter does not show the progress in the text field, this has to be accomplished manually:

widget.pulse()
widget.set_text("%d %%" % perc_value)

Levelbar

What set_fraction is for progressbar is set_value for levelbar. This is self-explanatory for the continous mode. In discrete mode the number of blocks have to be factored in:

widget.set_value(fraction*blocks)

For the add_offset_value function and colour schemes read the CSS article.

Listings

Glade

06_progresslevel.glade (Source)

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
  <requires lib="gtk+" version="3.20"/>
  <object class="GtkWindow" id="window">
    <property name="can_focus">False</property>
    <property name="default_width">400</property>
    <property name="default_height">150</property>
    <signal name="destroy" handler="on_window_destroy" swapped="no"/>
    <child>
      <object class="GtkBox">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="orientation">vertical</property>
        <property name="homogeneous">True</property>
        <child>
          <object class="GtkProgressBar" id="prog1">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="text" translatable="yes">Text hier</property>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">False</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkProgressBar" id="prog2">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="pulse_step">0.14999999999999999</property>
            <property name="show_text">True</property>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">False</property>
            <property name="position">1</property>
          </packing>
        </child>
        <child>
          <object class="GtkLevelBar" id="lev1">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">False</property>
            <property name="position">2</property>
          </packing>
        </child>
        <child>
          <object class="GtkLevelBar" id="lev2">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="max_value">5</property>
            <property name="mode">discrete</property>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">False</property>
            <property name="position">3</property>
          </packing>
        </child>
        <child>
          <object class="GtkButton" id="go">
            <property name="label" translatable="yes">Go!</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="receives_default">True</property>
            <property name="always_show_image">True</property>
            <signal name="clicked" handler="on_go_clicked" swapped="no"/>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">False</property>
            <property name="position">7</property>
          </packing>
        </child>
      </object>
    </child>
    <child>
      <placeholder/>
    </child>
  </object>
</interface>

Python

06_progresslevel.py (Source)

#!/usr/bin/python
# -*- coding: utf-8 -*-

import time

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


class Handler:

    def on_window_destroy(self, *args):
        Gtk.main_quit()

    def on_go_clicked(self, widget):
        for i in range(101):
            x.progbar1.set_fraction(i / 100)
            x.progbar2.pulse()
            x.progbar2.set_text("{} %".format(i))
            x.levbar1.set_value(i / 100)
            x.levbar2.set_value((i / 100) * 5)
            time.sleep(.05)
            #interrupt main loop to update GUI
            while Gtk.events_pending():
                Gtk.main_iteration()
        x.progbar2.set_fraction(1)


class Example:

    def __init__(self):

        self.builder = Gtk.Builder()
        self.builder.add_from_file("06_progresslevel.glade")
        self.builder.connect_signals(Handler())

        self.progbar1 = self.builder.get_object("prog1")
        self.progbar2 = self.builder.get_object("prog2")
        self.levbar1 = self.builder.get_object("lev1")
        self.levbar2 = self.builder.get_object("lev2")

        self.levbar2.add_offset_value("high", 4)
        self.levbar2.add_offset_value("full", 5)

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

    def main(self):
        Gtk.main()


x = Example()
x.main()

Serve the menu

Menus, toolbars and statusbars

Glade

/images/05_menutoolbar.thumbnail.png

Toolbar

Toolbars contain different widgets like buttons, togglebuttons, radiobuttons oder (sub) menus. Generating and editing toolbars is analogue to menus.

Statusbar

The purpose of statusbars is to show notifications or useful information. Messages are treated like a list, the statusbar widget provides the functions push and pop.

Python

To send a message to the statusbar you will just need the function

widget.push(content_id,message)

If messages are exclusively shown "on top" the content_id can be a random number, like 0.

Listings

Glade

05_menutoolbar.glade (Source)

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
  <requires lib="gtk+" version="3.20"/>
  <object class="GtkWindow" id="window">
    <property name="can_focus">False</property>
    <property name="resizable">False</property>
    <signal name="destroy" handler="on_window_destroy" swapped="no"/>
    <child>
      <object class="GtkBox">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="margin_bottom">9</property>
        <property name="orientation">vertical</property>
        <child>
          <object class="GtkMenuBar">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <child>
              <object class="GtkMenuItem">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes">_Datei</property>
                <property name="use_underline">True</property>
                <child type="submenu">
                  <object class="GtkMenu">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <child>
                      <object class="GtkImageMenuItem">
                        <property name="label">gtk-new</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                        <signal name="activate" handler="on_nothing_here" swapped="no"/>
                      </object>
                    </child>
                    <child>
                      <object class="GtkImageMenuItem">
                        <property name="label">gtk-open</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                        <signal name="activate" handler="on_nothing_here" swapped="no"/>
                      </object>
                    </child>
                    <child>
                      <object class="GtkImageMenuItem">
                        <property name="label">gtk-save</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                        <signal name="activate" handler="on_nothing_here" swapped="no"/>
                      </object>
                    </child>
                    <child>
                      <object class="GtkImageMenuItem">
                        <property name="label">gtk-save-as</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                        <signal name="activate" handler="on_nothing_here" swapped="no"/>
                      </object>
                    </child>
                    <child>
                      <object class="GtkSeparatorMenuItem">
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                      </object>
                    </child>
                    <child>
                      <object class="GtkImageMenuItem">
                        <property name="label">gtk-quit</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                        <signal name="activate" handler="on_window_destroy" swapped="no"/>
                        <accelerator key="q" signal="activate" modifiers="GDK_CONTROL_MASK"/>
                      </object>
                    </child>
                  </object>
                </child>
              </object>
            </child>
            <child>
              <object class="GtkMenuItem">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes">_Hilfe</property>
                <property name="use_underline">True</property>
                <child type="submenu">
                  <object class="GtkMenu">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <child>
                      <object class="GtkImageMenuItem" id="menu_info">
                        <property name="label">gtk-about</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                        <signal name="activate" handler="on_info_button_clicked" swapped="no"/>
                        <accelerator key="i" signal="activate" modifiers="GDK_CONTROL_MASK"/>
                      </object>
                    </child>
                  </object>
                </child>
              </object>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">1</property>
          </packing>
        </child>
        <child>
          <object class="GtkToolbar">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="toolbar_style">both</property>
            <child>
              <object class="GtkToolButton">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes">toolbutton</property>
                <property name="use_underline">True</property>
                <property name="stock_id">gtk-yes</property>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="homogeneous">True</property>
              </packing>
            </child>
            <child>
              <object class="GtkMenuToolButton">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes">menu</property>
                <property name="use_underline">True</property>
                <property name="stock_id">gtk-print</property>
                <child type="menu">
                  <object class="GtkMenu">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <child>
                      <object class="GtkMenuItem">
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="label" translatable="yes">submenuitem 1</property>
                        <property name="use_underline">True</property>
                      </object>
                    </child>
                    <child>
                      <object class="GtkMenuItem">
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="label" translatable="yes">submenuitem 2</property>
                        <property name="use_underline">True</property>
                      </object>
                    </child>
                  </object>
                </child>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="homogeneous">True</property>
              </packing>
            </child>
            <child>
              <object class="GtkToggleToolButton">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes">Toggle</property>
                <property name="use_underline">True</property>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="homogeneous">True</property>
              </packing>
            </child>
            <child>
              <object class="GtkSeparatorToolItem">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="homogeneous">True</property>
              </packing>
            </child>
            <child>
              <object class="GtkRadioToolButton" id="tb_radio1">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes">Radio</property>
                <property name="use_underline">True</property>
                <property name="active">True</property>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="homogeneous">True</property>
              </packing>
            </child>
            <child>
              <object class="GtkRadioToolButton">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes">Radio</property>
                <property name="use_underline">True</property>
                <property name="active">True</property>
                <property name="group">tb_radio1</property>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="homogeneous">True</property>
              </packing>
            </child>
            <child>
              <object class="GtkRadioToolButton">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes">Radio</property>
                <property name="use_underline">True</property>
                <property name="active">True</property>
                <property name="group">tb_radio1</property>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="homogeneous">True</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">2</property>
          </packing>
        </child>
        <child>
          <object class="GtkSeparator">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">3</property>
          </packing>
        </child>
        <child>
          <object class="GtkStatusbar" id="statusbar">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="margin_left">10</property>
            <property name="margin_right">10</property>
            <property name="margin_start">10</property>
            <property name="margin_end">10</property>
            <property name="margin_top">6</property>
            <property name="margin_bottom">6</property>
            <property name="orientation">vertical</property>
            <property name="spacing">2</property>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">4</property>
          </packing>
        </child>
      </object>
    </child>
    <child>
      <placeholder/>
    </child>
  </object>
  <object class="GtkAboutDialog" id="about_dialog">
    <property name="can_focus">False</property>
    <property name="title" translatable="yes">Info</property>
    <property name="resizable">False</property>
    <property name="modal">True</property>
    <property name="window_position">center-on-parent</property>
    <property name="destroy_with_parent">True</property>
    <property name="type_hint">dialog</property>
    <property name="deletable">False</property>
    <property name="transient_for">window</property>
    <property name="program_name">Info Dialog</property>
    <property name="version">0.1</property>
    <property name="comments" translatable="yes">Platz für mehr Blabla</property>
    <property name="website">www.example.com</property>
    <property name="logo_icon_name">image-missing</property>
    <child internal-child="vbox">
      <object class="GtkBox">
        <property name="can_focus">False</property>
        <property name="orientation">vertical</property>
        <property name="spacing">2</property>
        <child internal-child="action_area">
          <object class="GtkButtonBox">
            <property name="can_focus">False</property>
            <property name="layout_style">end</property>
            <child>
              <object class="GtkButton" id="close_button">
                <property name="label">gtk-close</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <property name="use_stock">True</property>
                <signal name="clicked" handler="on_close_button_clicked" swapped="no"/>
              </object>
              <packing>
                <property name="expand">True</property>
                <property name="fill">True</property>
                <property name="position">0</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">False</property>
            <property name="position">0</property>
          </packing>
        </child>
      </object>
    </child>
    <child>
      <placeholder/>
    </child>
  </object>
</interface>

Python

05_menutoolbar.py (Source)

#!/usr/bin/python
# -*- coding: utf-8 -*-

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


class Handler:

    def on_window_destroy(self, *args):
        Gtk.main_quit()

    def on_info_button_clicked(self, widget):
        x.sb_message("Öffne Info-Dialog")
        x.builder.get_object("about_dialog").show_all()

    def on_close_button_clicked(self, widget):
        x.sb_message("Schließe Info-Dialog")
        x.builder.get_object("about_dialog").hide_on_delete()

    def on_nothing_here(self, widget):
        x.sb_message("{}: Widget hat keine Funktion.".format(widget))


class Example:

    def __init__(self):

        self.builder = Gtk.Builder()
        self.builder.add_from_file("05_menutoolbar.glade")
        self.builder.connect_signals(Handler())

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

    def sb_message(self,message):
        self.builder.get_object("statusbar").push(0, message)

    def main(self):
        Gtk.main()


x = Example()
x.main()

Clickbaiting

Switch, checkbox, radiobutton - more elements to click on

This article explains the usage of control and display widgets on the basis of these selected elements. The usage follows the pattern:

  1. create container (box, toolbar etc.) for widget

  2. add element

  3. add an identifier to the element (that step canbe skipped for elements that do not need to be addressed in the source code like boxes or separators)

  4. assign a function to a signal

  5. (optional) test signal emission in Glade preview window

  6. code funtion

All available GTK+ classes and their functions are documented in the Python GI API Reference >> Gtk 3.0 >> Classes.

/images/04_clickableelements.thumbnail.png

Glade

Switch

A switch is a widget that posseses two states, on and off. The current status can be retrieved by the state_set signal which is emitted on turning the switch on or off.

Checkbox

Checkboxes are basically just togglebuttons therefore the toggled signal is allocated.

Radiobutton

The purpose of radiobuttons is the selection of _one_ list item. The widget is also a sub class of GtkToggleButton (allocate toggled signal).

Every radio button is a member of a group. This is done via "General > Button Attributes > Group". There is one 'leading' radiobutton that are all other radiobuttons bound to.

Python

Given that checkboxes and radiobuttons are togglebuttons the status is retrieved by the widget.get_active() function.

When the state_set signal is emitted on the switch a parameter is passed containing the status as boolean (True/False).

def on_switch_state_set(self,widget,state):
    if state is True:
        print("switch is on")
    else:
        print("switch is off")

Listings

Glade

04_clickableelements.glade (Source)

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
  <requires lib="gtk+" version="3.20"/>
  <object class="GtkWindow" id="window">
    <property name="can_focus">False</property>
    <property name="default_width">150</property>
    <signal name="destroy" handler="on_window_destroy" swapped="no"/>
    <child>
      <object class="GtkBox">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="margin_left">4</property>
        <property name="margin_right">4</property>
        <property name="margin_top">4</property>
        <property name="margin_bottom">4</property>
        <property name="orientation">vertical</property>
        <property name="spacing">1</property>
        <child>
          <object class="GtkSwitch" id="switch">
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="active">True</property>
            <signal name="state-set" handler="on_switch_state_set" swapped="no"/>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkCheckButton" id="cbutton">
            <property name="label" translatable="yes">Checkbox</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="receives_default">False</property>
            <property name="active">True</property>
            <property name="draw_indicator">True</property>
            <signal name="toggled" handler="on_cbutton_toggled" swapped="no"/>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">2</property>
          </packing>
        </child>
        <child>
          <object class="GtkSeparator">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">3</property>
          </packing>
        </child>
        <child>
          <object class="GtkRadioButton" id="rbutton1">
            <property name="label" translatable="yes">Alternative 1</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="receives_default">False</property>
            <property name="active">True</property>
            <property name="draw_indicator">True</property>
            <signal name="toggled" handler="on_rbutton1_toggled" swapped="no"/>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">4</property>
          </packing>
        </child>
        <child>
          <object class="GtkRadioButton" id="rbutton2">
            <property name="label" translatable="yes">Alternative 2</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="receives_default">False</property>
            <property name="active">True</property>
            <property name="draw_indicator">True</property>
            <property name="group">rbutton1</property>
            <signal name="toggled" handler="on_rbutton2_toggled" swapped="no"/>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">5</property>
          </packing>
        </child>
        <child>
          <object class="GtkRadioButton" id="rbutton3">
            <property name="label" translatable="yes">Alternative 3</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="receives_default">False</property>
            <property name="active">True</property>
            <property name="draw_indicator">True</property>
            <property name="group">rbutton1</property>
            <signal name="toggled" handler="on_rbutton3_toggled" swapped="no"/>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">6</property>
          </packing>
        </child>
      </object>
    </child>
    <child>
      <placeholder/>
    </child>
  </object>
</interface>

Python

04_clickableelements.py (Source)

#!/usr/bin/python
# -*- coding: utf-8 -*-

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


class Handler:

    def on_window_destroy(self, *args):
        Gtk.main_quit()

    def on_switch_state_set(self, widget, state):
        if state is True:
            print("switch is on")
        else:
            print("switch is off")

    def on_cbutton_toggled(self, widget):
        if widget.get_active():
            print("checkbox checked")
        else:
            print("checkbox unchecked")

    def on_rbutton1_toggled(self, widget):
        if widget.get_active():
            print("radiobutton selection changed to 1")

    def on_rbutton2_toggled(self, widget):
        if widget.get_active():
            print("radiobutton selection changed to 2")

    def on_rbutton3_toggled(self, widget):
        if widget.get_active():
            print("radiobutton selection changed to 3")


class Example:

    def __init__(self):

        self.gladefile = "04_clickableelements.glade"
        self.builder = Gtk.Builder()
        self.builder.add_from_file(self.gladefile)
        self.builder.connect_signals(Handler())

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

    def main(self):
        Gtk.main()


x = Example()
x.main()

Open and close windows

Contents

Open and close windows and dialogues

Glade

There can be created different types of windows and dialogs (which are subclasses of windows). In this example the main window has two buttons. The first one opens an info dialog, the second one closes the main window and opens another window instead.

/images/03_changewindow.thumbnail.png

7 Signals are assigned:

  • (per) window
    • "info" button (headerbar left): clicked

    • "ok" button (headerbar right): clicked

    • close/quit: destroy

  • info dialog
    • close/quit: destroy

03_changewindow.glade (Source)

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 -->
<interface>
  <requires lib="gtk+" version="3.20"/>
  <object class="GtkWindow" id="win1">
    <property name="can_focus">False</property>
    <property name="resizable">False</property>
    <signal name="destroy" handler="on_window_destroy" swapped="no"/>
    <child type="titlebar">
      <object class="GtkHeaderBar">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="title">Fenster 1</property>
        <property name="subtitle">Untertitel</property>
        <property name="show_close_button">True</property>
        <child>
          <object class="GtkButton" id="info_button1">
            <property name="label">gtk-about</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="receives_default">True</property>
            <property name="use_stock">True</property>
            <property name="always_show_image">True</property>
            <signal name="clicked" handler="on_info_button_clicked" swapped="no"/>
          </object>
          <packing>
            <property name="position">1</property>
          </packing>
        </child>
        <child>
          <object class="GtkButton" id="button1">
            <property name="label">gtk-ok</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="receives_default">True</property>
            <property name="use_stock">True</property>
            <property name="always_show_image">True</property>
            <signal name="clicked" handler="on_button1_clicked" swapped="no"/>
          </object>
          <packing>
            <property name="pack_type">end</property>
            <property name="position">1</property>
          </packing>
        </child>
      </object>
    </child>
    <child>
      <object class="GtkBox">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="margin_bottom">9</property>
        <property name="orientation">vertical</property>
        <property name="spacing">10</property>
        <child>
          <object class="GtkLabel" id="label1">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="label" translatable="yes">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis placerat, dui at sollicitudin mollis, lectus risus porttitor felis, sed malesuada purus eros sit amet magna. Nunc consectetur rutrum gravida. Mauris sed enim vitae orci mattis pretium eu interdum arcu. Morbi sed enim non erat bibendum tincidunt. Aenean nunc nisl, sagittis sit amet tellus ac, condimentum ullamcorper mi. Cras ornare faucibus laoreet. Quisque quis sagittis est, et hendrerit libero.</property>
            <property name="wrap">True</property>
            <property name="max_width_chars">80</property>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">1</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
  <object class="GtkAboutDialog" id="about_dialog">
    <property name="can_focus">False</property>
    <property name="title" translatable="yes">Info</property>
    <property name="resizable">False</property>
    <property name="modal">True</property>
    <property name="window_position">center-on-parent</property>
    <property name="destroy_with_parent">True</property>
    <property name="type_hint">dialog</property>
    <property name="deletable">False</property>
    <property name="transient_for">win1</property>
    <property name="program_name">Info Dialog</property>
    <property name="version">0.1</property>
    <property name="comments" translatable="yes">Platz für mehr Blabla</property>
    <property name="website">www.example.com</property>
    <property name="logo_icon_name">image-missing</property>
    <child>
      <placeholder/>
    </child>
    <child internal-child="vbox">
      <object class="GtkBox">
        <property name="can_focus">False</property>
        <property name="orientation">vertical</property>
        <property name="spacing">2</property>
        <child internal-child="action_area">
          <object class="GtkButtonBox">
            <property name="can_focus">False</property>
            <property name="layout_style">end</property>
            <child>
              <object class="GtkButton" id="close_button">
                <property name="label">gtk-close</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <property name="use_stock">True</property>
                <signal name="clicked" handler="on_close_button_clicked" swapped="no"/>
              </object>
              <packing>
                <property name="expand">True</property>
                <property name="fill">True</property>
                <property name="position">0</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">False</property>
            <property name="position">0</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
  <object class="GtkWindow" id="win2">
    <property name="can_focus">False</property>
    <property name="resizable">False</property>
    <signal name="destroy" handler="on_window_destroy" swapped="no"/>
    <child type="titlebar">
      <object class="GtkHeaderBar">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="title">Fenster 2 </property>
        <property name="subtitle">Anderer Untertitel</property>
        <property name="show_close_button">True</property>
        <child>
          <object class="GtkButton" id="info_button2">
            <property name="label">gtk-about</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="receives_default">True</property>
            <property name="use_stock">True</property>
            <property name="always_show_image">True</property>
            <signal name="clicked" handler="on_info_button_clicked" swapped="no"/>
          </object>
          <packing>
            <property name="position">1</property>
          </packing>
        </child>
        <child>
          <object class="GtkButton" id="button2">
            <property name="label">gtk-ok</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="receives_default">True</property>
            <property name="use_stock">True</property>
            <property name="always_show_image">True</property>
            <signal name="clicked" handler="on_button2_clicked" swapped="no"/>
          </object>
          <packing>
            <property name="pack_type">end</property>
            <property name="position">1</property>
          </packing>
        </child>
      </object>
    </child>
    <child>
      <object class="GtkBox">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="margin_bottom">9</property>
        <property name="orientation">vertical</property>
        <property name="spacing">10</property>
        <child>
          <object class="GtkLabel" id="label2">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="label" translatable="yes">In sagittis purus nec eleifend dignissim. Curabitur venenatis eleifend leo ac tincidunt. Etiam ut consequat neque. Aenean in libero placerat, iaculis est quis, blandit nulla. Nulla euismod cursus nisl efficitur imperdiet. Sed vel augue vitae dui congue eleifend id eu libero. Cras laoreet velit nibh, et pharetra ante pharetra id. Nullam mollis arcu a nibh pulvinar, sed volutpat quam facilisis. Vivamus quis leo quis orci aliquam fermentum. Donec varius accumsan nisi eu ullamcorper. Integer condimentum, eros sit amet convallis vehicula, elit leo mattis risus, quis suscipit turpis nibh sed nulla. Sed id justo ut magna commodo eleifend. Praesent nunc arcu, elementum eu dolor nec, rutrum molestie mauris.</property>
            <property name="wrap">True</property>
            <property name="max_width_chars">80</property>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">1</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
</interface>

Python

The most important functions needed for handling windows are

# open window
Gtk.Builder.get_object("name").show_all()
# close window temporarily, can be retrieved by show_all()
Gtk.Builder.get_object("name").hide_on_delete()
# close window, quit Gtk
Gtk.main_quit()

Functions within the Handler() class of course can be assigned to multiple signals. So there are only 5 handler functions required in this example file.

The complete example code is:

03_changewindow.py (Source)

#!/usr/bin/python
# -*- coding: utf-8 -*-

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


class Handler:

    def on_window_destroy(self, *args):
        Gtk.main_quit()

    def on_button1_clicked(self, widget):
        x.window.hide_on_delete()
        x.set_window("win2")

    def on_button2_clicked(self, widget):
        x.window.hide_on_delete()
        x.set_window("win1")

    def on_info_button_clicked(self, widget):
        x.about.show_all()

    def on_close_button_clicked(self, widget):
        x.about.hide_on_delete()


class Example:

    def __init__(self):

        self.builder = Gtk.Builder()
        self.builder.add_from_file("03_changewindow.glade")
        self.builder.connect_signals(Handler())

        self.about = self.builder.get_object("about_dialog")

        self.set_window("win1")

    def set_window(self, win):
        self.window = self.builder.get_object(win)
        self.window.show_all()

    def main(self):
        Gtk.main()


x = Example()
x.main()

See also the sequel article to dialogues.

Push the button

Buttons and labels

Glade

Based on the first example some elements are added, a label, a button and a togglebutton. Any control or display widget requires a container. In this example a vertically arranged container box is created. Boxes can be extended, downsized, rearranged and contain further container widgets.

Now the clicked and toggled signal are assigned to the button and togglebutton element. The label widget's purpose ist to display text so there is no signal assignment required.

Glade provides a preview function to test if the correct reaction is triggered.

/images/02_gladepreview.thumbnail.png

Python

On a button click the label widget should change the diplayed text randomly chosen from a given list.

All Gtk.Builder elements can be addressed by the get_object function.

Gtk.Builder.get_object("name").function(options)

#Beispiel GtkLabel
Gtk.Builder.get_object("label_name").set_text("new text")

The task of the togglebutton is to clear the label widget display and deactivate the button which can be reactivated on the next click.

The status of togglebuttons can be retrieved by the get_active() function returning True or False.

Depending on the widget the corresponding signal function expects at least one parameter

def on_t_button_toggled(self,widget):
    if widget.get_active():
        #do something
    else:
        #do something different

Listings

Glade

02_labelbutton.glade (Source)

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
  <requires lib="gtk+" version="3.20"/>
  <object class="GtkImage" id="image1">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    <property name="stock">gtk-dialog-error</property>
  </object>
  <object class="GtkApplicationWindow" id="window">
    <property name="width_request">500</property>
    <property name="can_focus">False</property>
    <property name="title" translatable="yes">Titel</property>
    <property name="resizable">False</property>
    <signal name="destroy" handler="on_window_destroy" swapped="no"/>
    <child>
      <object class="GtkBox">
        <property name="width_request">200</property>
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="orientation">vertical</property>
        <property name="homogeneous">True</property>
        <child>
          <object class="GtkLabel" id="label">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="label" translatable="yes">Label-Text kann in Glade unter "Beschriftung" eingegeben werden</property>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkButton" id="button">
            <property name="label" translatable="yes">Label-Text ändern</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="receives_default">True</property>
            <signal name="clicked" handler="on_button_clicked" swapped="no"/>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">1</property>
          </packing>
        </child>
        <child>
          <object class="GtkToggleButton" id="t_button">
            <property name="label" translatable="yes">Oberen Button sperren</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="receives_default">True</property>
            <property name="image">image1</property>
            <property name="image_position">top</property>
            <property name="always_show_image">True</property>
            <signal name="toggled" handler="on_t_button_toggled" swapped="no"/>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">2</property>
          </packing>
        </child>
      </object>
    </child>
    <child>
      <placeholder/>
    </child>
  </object>
</interface>

Python

02_labelbutton.py (Source)

#!/usr/bin/python
# -*- coding: utf-8 -*-

import random

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


class Handler:

    def on_window_destroy(self, *args):
        Gtk.main_quit()

    def on_button_clicked(self,widget):
        new_text = random.choice(x.label_texts)
        x.builder.get_object("label").set_text(new_text)

    def on_t_button_toggled(self,widget):
        if widget.get_active():
            x.builder.get_object("label").set_text("")
            x.builder.get_object("button").set_sensitive(False)
        else:
            x.builder.get_object("button").set_sensitive(True)


class Example:

    def __init__(self):

        self.gladefile = "02_labelbutton.glade"

        self.label_texts = ["The things you used to own, now they own you.",
                            "I am Jack's complete lack of surprise. I am Jack's Broken Heart.",
                            "On a long enough time line, the survival rate for everyone drops to zero.",
                            "Sticking feathers up your butt does not make you a chicken!",
                            "I am Jack's smirking revenge."]

        self.builder = Gtk.Builder()
        self.builder.add_from_file(self.gladefile)
        self.builder.connect_signals(Handler())

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

    def main(self):
        Gtk.main()


x = Example()
x.main()

View from the window

Minimal example

Glade

After launching Glade the application view is divided in 3 areas: window/widget selection on the left, the project view in the centre and a tree/property (including signal) view of project elements on the right.

Now a window is created and an ID assigned to be able to address the element in the source code.

/images/01_glade.thumbnail.png

To call functions from widgets they have to be connected to signals. Depending on the kind of graphical object signals can be emitted by clicking, marking, editing, switching etc.

If the example window should be able to be closed on clicking the [X] button the signal destroy is required. The entry field to specify the function provides a convenient suggestion function to reduce key input following the pattern on_id_signal.

/images/01_destroysignal.thumbnail.png

Glade does not generate GTK+ source code but a XML formatted file accessible to GtkBuilder (see listing below).

Python

First things first. GtkBuilder is provided by the Gtk module from the Python GObject Introspection bindings:

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

After initing Gtk.Builder() the Glade file(s) are added.

builder.add_from_file(gladefile)

It may be convenient to work with multiple files in one project. In this case you have to bear in mind that if there are elements with the same identifier name only the element of the last loaded file can be addressed by get_object(id).

Second step is connecting the signals. It comes in handy to store these functions in an own class:

self.builder.connect_signals(Handler())

So this basic example script opens an empty window hat can be closed on clicking the close button.

Ohne Glade

This example corresponds to the basic example of the Python GTK+ 3 tutorial:

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

win = Gtk.Window()
win.connect("delete-event", Gtk.main_quit)
win.show_all()
Gtk.main()

The definded window does not contain any elements like boxes, buttons, bars, menus and other widgets.

Listings

Glade

01_minimal.glade (Source)

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
  <requires lib="gtk+" version="3.20"/>
  <object class="GtkApplicationWindow" id="window">
    <property name="can_focus">False</property>
    <property name="title" translatable="yes">Titel</property>
    <signal name="destroy" handler="on_window_destroy" swapped="no"/>
    <child>
      <placeholder/>
    </child>
    <child>
      <placeholder/>
    </child>
  </object>
</interface>

Python

01_minimal.py (Source)

#!/usr/bin/python
# -*- coding: utf-8 -*-

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


class Handler:

    def on_window_destroy(self, *args):
        Gtk.main_quit()


class Example:

    def __init__(self):

        self.builder = Gtk.Builder()
        self.builder.add_from_file("01_minimal.glade")
        self.builder.connect_signals(Handler())

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

    def main(self):
        Gtk.main()


x = Example()
x.main()

Glade tutorial series

Motivation

Glade was my weapon of choice when I started creating the graphical interface for gpt and NoN. It itself is a GUI for creating GTK+ <http://www.gtk.org/> GUIs.

Glade project files are XML formatted GtkBuilder files. The connection to the source code is established by signals. Numerous languages are supported, I will use Python here.

There are plenty of tutorials and docs around that are not or only partly working or valid due to recent development progress (GTK+ and Python from 2.x to 3.x) but for the most part they are a good start (see links below).

I attempt to keep this basic tutorial up to date (Python 3.5.2 (3.6 just hit my machine and there aren't any problems so far but I will have a closer look on the examples) and Glade 3.20.0).

Non-exclusive

Of course GTK+ elements can be directly constructed in the source code. It is also possible to use both ways simultaneously or replace one with the other during development process.

The fact that Glade supports various programming languages it may also be possible to create applications in different programming languages using the same graphical interface.

Todo

  • WebKit2

  • Interaction between other applications