GTK GUI with PyGObject (3)

Date: 2021/07/16 (initial publish), 2021/07/24 (last update)

Source: en/note-00016.md

Previous Post Top Next Post

TOC

Example: Special button widgets into GUI

To put more widgets,“Number of items” on the left panel of GtkBox was incremented. Some box are further subdivided again with GtkBox with their “Orientation” settings set to “Horizontal” instead.

Then I placed several additional simple “Control” widgets:

Please note I created multiple widgets to learn how they work together with the object.

I set IDs for these widgets basically by dropping “Gtk” from the GTk class names with optional number as suffix to distinguish them. Then set up signal handlers in the right panel.

ID/Class name Widget Signal Signal handler
simple GtkWindow destroy onDestroy
button GtkButton pressed onButtonPressed
entry GtkEntry changed onEntryChanged
spinbutton? GtkSpinButton changed onSpinButtonChanged?
togglebutton? GtkToggleButton toggled onToggleButtonToggled?
checkbutton? GtkCheckButton toggled onCheckButtonToggled?
radiobutton? GtkRadioButton toggled onRadioButtonToggled
filechooserbutton GtkFileChooserButton selection-changed onFileChooserButtonChanged
label GtkLabel

There are some non-obvious extra works other with Glade usage.

GtkSpinButton widget needs to click on the pen-icon in the “Adjustment” entry box to open dialog and create a new Adjustment object “adjustment1” for spinbutton1. Although normally each spinbutton? has its own adjustment?", just for learning, let me set the same Adjustment object “adjustment1” for spinbutton2. These 2 spin buttons now function in parallel since they share the same Adjustment object “adjustment1”.

GtkToggleButton widget can display icon instead of just a text label by clicking “Always show image” checkbox and clicking on the pen-icon in the “Image” entry box to open dialog and create a new GtkImage object “image1” for togglebutton1. When setting “Image”, somehow my Glade focus on deprecated “Stock ID”. Just click radio button for “Icon name” and select one you like. Do the same for another to create a new GtkImage object “image2”. If you put null string to the text label, this becomes a clean small icon button.

GtkCheckButton widget is very much like GtkToggleButton except for presentation of its state and area to click.

GtkRadioButton widget with round icon is visually very much like GtkCheckButton with square icon but has quite unique feature of exclusivity. This is realized by grouping these controls. Click on the pen-icon in the “Group” entry box for RadioButton2 and RadioButton3 widgets and select RadioButton1 as their Group choice.

GtkFileChooserButton widget has 2 modes of operations determined by “FileChooserAttribute” to be either “Open” or “Select Folder”. Let’s set this to “Select Folder”. (Somehow my Glade version 3.38.2 tends to skip updating menu. So I end up setting it multiple times and checking the resulting XML file for <property name="action">select-folder</property> by a text editor.)

The Glade screen

How to create GUI program

Now we are ready to use this from simple2.py:

import gi

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


@Gtk.Template(filename="simple2.ui")
class SimpleWindow(Gtk.Window):
    # corresponding name in XML 'class' attribute for this class
    __gtype_name__ = "simple2"
    # corresponding name in XML 'id' attribute sets this class member variable
    label = Gtk.Template.Child()
    # class member variables
    entry_text = "(null as init)"
    spin1_text = "0 (init)"
    spin2_text = "0 (init)"
    toggle1_text = "___ off ___ (init)"
    toggle2_text = "___ off ___ (init)"
    check1_text = "___ off ___ (init)"
    check2_text = "___ off ___ (init)"
    radio_text = "radiobutton1 (init)"
    folder = "(null as init)"

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

    @Gtk.Template.Callback()
    def onButtonPressed(self, widget):
        print("=" * 80)
        print("Button label:    " + widget.get_label())
        print("Entered text:    " + self.entry_text)
        print("Spin1 value :    " + self.spin1_text)
        print("Spin2 value :    " + self.spin2_text)
        print("Toggle1 text:    " + self.toggle1_text)
        print("Toggle2 text:    " + self.toggle2_text)
        print("Check1 text :    " + self.check1_text)
        print("Check2 text :    " + self.check2_text)
        print("Radio text  :    " + self.radio_text)
        print("Folder      :    " + self.folder)
        # This is right, I think.
        self.label.set_label(self.entry_text)

    @Gtk.Template.Callback()
    def onEntryChanged(self, widget):
        self.entry_text = widget.get_text()

    @Gtk.Template.Callback()
    def onSpinButton1Changed(self, widget):
        self.spin1_text = str(widget.get_value_as_int())

    @Gtk.Template.Callback()
    def onSpinButton2Changed(self, widget):
        self.spin2_text = str(widget.get_value_as_int())

    @Gtk.Template.Callback()
    def onToggleButton1Toggled(self, widget):
        if widget.get_active():
            self.toggle1_text = "@@@ ON  @@@"
        else:
            self.toggle1_text = "___ off ___"

    @Gtk.Template.Callback()
    def onToggleButton2Toggled(self, widget):
        if widget.get_active():
            self.toggle2_text = "@@@ ON  @@@"
        else:
            self.toggle2_text = "___ off ___"

    @Gtk.Template.Callback()
    def onCheckButton1Toggled(self, widget):
        if widget.get_active():
            self.check1_text = "@@@ ON  @@@"
        else:
            self.check1_text = "___ off ___"

    @Gtk.Template.Callback()
    def onCheckButton2Toggled(self, widget):
        if widget.get_active():
            self.check2_text = "@@@ ON  @@@"
        else:
            self.check2_text = "___ off ___"

    @Gtk.Template.Callback()
    def onRadioButtonToggled(self, widget):
        if widget.get_active():
            self.radio_text = widget.get_label()

    @Gtk.Template.Callback()
    def onFileChooserButtonSet(self, widget):
        self.folder = widget.get_filename()

window = SimpleWindow()
window.show()

Gtk.main()

Running this with python3 simple2.py will start the GUI as:

Run simple2

Please play with this GUI while looking at your terminal outputs.

How Glade generated XML looks

Just in case I forget which items were selected via Glade session, here is the Glade generated XML simple2.ui:

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface>
  <requires lib="gtk+" version="3.24"/>
  <object class="GtkAdjustment" id="adjustment1">
    <property name="upper">100</property>
    <property name="step-increment">1</property>
    <property name="page-increment">10</property>
  </object>
  <object class="GtkImage" id="image1">
    <property name="visible">True</property>
    <property name="can-focus">False</property>
    <property name="icon-name">format-justify-left</property>
  </object>
  <object class="GtkImage" id="image2">
    <property name="visible">True</property>
    <property name="can-focus">False</property>
    <property name="icon-name">format-justify-fill</property>
  </object>
  <template class="simple2" parent="GtkWindow">
    <property name="can-focus">False</property>
    <signal name="destroy" handler="onDestroy" 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="GtkButton" id="button">
            <property name="label" translatable="yes">Press this to update</property>
            <property name="visible">True</property>
            <property name="can-focus">True</property>
            <property name="receives-default">True</property>
            <signal name="pressed" handler="onButtonPressed" swapped="no"/>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkEntry" id="entry">
            <property name="visible">True</property>
            <property name="can-focus">True</property>
            <signal name="changed" handler="onEntryChanged" swapped="no"/>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">1</property>
          </packing>
        </child>
        <child>
          <object class="GtkLabel" id="label">
            <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">2</property>
          </packing>
        </child>
        <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="spinbutton1">
                <property name="visible">True</property>
                <property name="can-focus">True</property>
                <property name="adjustment">adjustment1</property>
                <signal name="changed" handler="onSpinButton1Changed" swapped="no"/>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">0</property>
              </packing>
            </child>
            <child>
              <object class="GtkSpinButton" id="spinbutton2">
                <property name="visible">True</property>
                <property name="can-focus">True</property>
                <property name="adjustment">adjustment1</property>
                <signal name="changed" handler="onSpinButton2Changed" swapped="no"/>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">1</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">3</property>
          </packing>
        </child>
        <child>
          <object class="GtkBox">
            <property name="visible">True</property>
            <property name="can-focus">False</property>
            <property name="homogeneous">True</property>
            <child>
              <object class="GtkToggleButton" id="togglebutton1">
                <property name="label" translatable="yes">togglebutton1</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="always-show-image">True</property>
                <signal name="toggled" handler="onToggleButton1Toggled" swapped="no"/>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">0</property>
              </packing>
            </child>
            <child>
              <object class="GtkToggleButton" id="togglebutton2">
                <property name="label" translatable="yes">togglebutton2</property>
                <property name="visible">True</property>
                <property name="can-focus">True</property>
                <property name="receives-default">True</property>
                <property name="image">image2</property>
                <property name="always-show-image">True</property>
                <signal name="toggled" handler="onToggleButton2Toggled" swapped="no"/>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">1</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">4</property>
          </packing>
        </child>
        <child>
          <object class="GtkBox">
            <property name="visible">True</property>
            <property name="can-focus">False</property>
            <property name="homogeneous">True</property>
            <child>
              <object class="GtkCheckButton" id="checkbutton1">
                <property name="label" translatable="yes">checkbutton1</property>
                <property name="visible">True</property>
                <property name="can-focus">True</property>
                <property name="receives-default">False</property>
                <property name="draw-indicator">True</property>
                <signal name="toggled" handler="onCheckButton1Toggled" 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="checkbutton2">
                <property name="label" translatable="yes">checkbutton2</property>
                <property name="visible">True</property>
                <property name="can-focus">True</property>
                <property name="receives-default">False</property>
                <property name="draw-indicator">True</property>
                <signal name="toggled" handler="onCheckButton2Toggled" swapped="no"/>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">1</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">5</property>
          </packing>
        </child>
        <child>
          <object class="GtkBox">
            <property name="visible">True</property>
            <property name="can-focus">False</property>
            <property name="homogeneous">True</property>
            <child>
              <object class="GtkRadioButton" id="radiobutton1">
                <property name="label" translatable="yes">radiobutton1</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="onRadioButtonToggled" swapped="no"/>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">0</property>
              </packing>
            </child>
            <child>
              <object class="GtkRadioButton" id="radiobutton2">
                <property name="label" translatable="yes">radiobutton2</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">radiobutton1</property>
                <signal name="toggled" handler="onRadioButtonToggled" swapped="no"/>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">1</property>
              </packing>
            </child>
            <child>
              <object class="GtkRadioButton" id="radiobutton3">
                <property name="label" translatable="yes">radiobutton3</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">radiobutton1</property>
                <signal name="toggled" handler="onRadioButtonToggled" swapped="no"/>
              </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">6</property>
          </packing>
        </child>
        <child>
          <object class="GtkFileChooserButton" id="filechooserbutton">
            <property name="visible">True</property>
            <property name="can-focus">False</property>
            <property name="action">select-folder</property>
            <property name="title" translatable="yes"/>
            <signal name="file-set" handler="onFileChooserButtonSet" swapped="no"/>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">7</property>
          </packing>
        </child>
      </object>
    </child>
  </template>
</interface>
Previous Post Top Next Post