GTK GUI with PyGObject (6)

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

Source: en/note-00019.md

Previous Post Top Next Post

TOC

GtkComboBox and GtkComboBoxText

A GtkComboBox is a widget that allows the user to choose from a list of valid choices offered in Gtk.TreeModel.

GtkComboBoxText is for the simpler text-only case of GtkComboBox.

For these let’s create combo-box.py/combo-box.ui and combo-box-text.py/combo-box-text.ui examples and compare them. It took me a while to make these working since existing examples usually don’t use Glade nor Gtk.Template.

Example: GtkComboBox

Since GUI combobox doesn’t know how to render the model-provided data to GUI, it needs to be explicitly specified and becomes complex.

combo-box.py:

import gi

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

@Gtk.Template(filename="combo-box.ui")
class SimpleWindow(Gtk.Window):

    # corresponding name in XML 'class' attribute for this class
    __gtype_name__ = "combo_boxes"
    # corresponding name in XML 'id' attribute sets this class member variable
    liststore_country = Gtk.Template.Child() # MODEL
    liststore_lang1 = Gtk.Template.Child()   # MODEL
    combo_country = Gtk.Template.Child()     # VIEW
    combo_lang1 = Gtk.Template.Child()       # VIEW
    button = Gtk.Template.Child()

    def __init__(self):
        super().__init__()

        renderer_text = Gtk.CellRendererText()
        # pack_start(cell, expand), Here, cell (Gtk.CellRenderer) 
        self.combo_country.pack_start(renderer_text, True)
        # add_attribute (cell, attribute, column)
        self.combo_country.add_attribute(renderer_text, "text", 0)

        tree_iter = self.combo_country.get_active_iter()
        self.country = self.liststore_country[tree_iter][0]

        self.combo_lang1.pack_start(renderer_text, True)
        self.combo_lang1.add_attribute(renderer_text, "text", 0)

        tree_iter = self.combo_lang1.get_active_iter()
        self.lang1 = self.liststore_lang1[tree_iter][0]

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

    @Gtk.Template.Callback()
    def onCountryChanged(self, widget):
        tree_iter = widget.get_active_iter()
        model = widget.get_model()
        country = model[tree_iter][0]
        print("Selected: country = '{}'".format(country))
        self.country = country

    @Gtk.Template.Callback()
    def onLang1Changed(self, widget):
        tree_iter = widget.get_active_iter()
        model = widget.get_model()
        lang1 = model[tree_iter][0]
        print("Selected:                    lang1 = '{}'".format(lang1))
        self.lang1 = lang1

    @Gtk.Template.Callback()
    def onButtonPressed(self, widget):
        print("=" * 80)
        print("country = '{}', lang1 = '{}'".format(self.country, self.lang1))
        print("~" * 80)

window = SimpleWindow()
window.show()
Gtk.main()

combo-box.ui:

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface>
  <requires lib="gtk+" version="3.24"/>
  <object class="GtkListStore" id="liststore_country">
    <columns>
      <!-- column-name country -->
      <column type="gchararray"/>
    </columns>
    <data>
      <row>
        <col id="0" translatable="yes">U.S.A</col>
      </row>
      <row>
        <col id="0" translatable="yes">Japan</col>
      </row>
      <row>
        <col id="0" translatable="yes">Mexico</col>
      </row>
      <row>
        <col id="0" translatable="yes">Taiwan (R.O.C)</col>
      </row>
      <row>
        <col id="0" translatable="yes">France</col>
      </row>
      <row>
        <col id="0" translatable="yes">U.K.</col>
      </row>
      <row>
        <col id="0" translatable="yes">Canada</col>
      </row>
    </data>
  </object>
  <object class="GtkListStore" id="liststore_lang1">
    <columns>
      <!-- column-name Language -->
      <column type="gchararray"/>
    </columns>
    <data>
      <row>
        <col id="0" translatable="yes">Japanese</col>
      </row>
      <row>
        <col id="0" translatable="yes">English</col>
      </row>
      <row>
        <col id="0" translatable="yes">Mandarin Chinese</col>
      </row>
      <row>
        <col id="0" translatable="yes">French</col>
      </row>
      <row>
        <col id="0" translatable="yes">Spanish</col>
      </row>
    </data>
  </object>
  <template class="combo_boxes" parent="GtkWindow">
    <property name="can-focus">False</property>
    <property name="title" translatable="yes">COMBOBOX EXAMPLES</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>
        <property name="baseline-position">top</property>
        <child>
          <object class="GtkLabel" id="label_county">
            <property name="visible">True</property>
            <property name="can-focus">False</property>
            <property name="label" translatable="yes">Country</property>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkComboBox" id="combo_country">
            <property name="visible">True</property>
            <property name="can-focus">False</property>
            <property name="model">liststore_country</property>
            <property name="active">0</property>
            <property name="id-column">0</property>
            <signal name="changed" handler="onCountryChanged" 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_lang1">
            <property name="visible">True</property>
            <property name="can-focus">False</property>
            <property name="label" translatable="yes">Language (primary)</property>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">2</property>
          </packing>
        </child>
        <child>
          <object class="GtkComboBox" id="combo_lang1">
            <property name="visible">True</property>
            <property name="can-focus">False</property>
            <property name="model">liststore_lang1</property>
            <property name="active">0</property>
            <property name="id-column">0</property>
            <signal name="changed" handler="onLang1Changed" swapped="no"/>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">3</property>
          </packing>
        </child>
        <child>
          <object class="GtkButton" id="button">
            <property name="label" translatable="yes">Print</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">4</property>
          </packing>
        </child>
      </object>
    </child>
  </template>
</interface>

Example: GtkComboBoxText

Since GUI combobox created by GtkComboBoxText knows the model-provided data is text and how to render to GUI, program is simpler and XML needs minimal changes.

combo-boxi-text.py:

import gi

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

@Gtk.Template(filename="combo-box-text.ui")
class SimpleWindow(Gtk.Window):

    # corresponding name in XML 'class' attribute for this class
    __gtype_name__ = "combo_boxes"
    # corresponding name in XML 'id' attribute sets this class member variable
    liststore_country = Gtk.Template.Child() # MODEL
    liststore_lang1 = Gtk.Template.Child()   # MODEL
    combo_country = Gtk.Template.Child()     # VIEW
    combo_lang1 = Gtk.Template.Child()       # VIEW
    button = Gtk.Template.Child()

    def __init__(self):
        super().__init__()
        self.country = self.combo_country.get_active_text()
        self.lang1 = self.combo_lang1.get_active_text()

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

    @Gtk.Template.Callback()
    def onCountryChanged(self, widget):
        tree_iter = widget.get_active_iter()
        model = widget.get_model()
        country = model[tree_iter][0]
        print("Selected: country = '{}'".format(country))
        self.country = country

    @Gtk.Template.Callback()
    def onLang1Changed(self, widget):
        tree_iter = widget.get_active_iter()
        model = widget.get_model()
        lang1 = model[tree_iter][0]
        print("Selected:                    lang1 = '{}'".format(lang1))
        self.lang1 = lang1

    @Gtk.Template.Callback()
    def onButtonPressed(self, widget):
        print("=" * 80)
        print("country = '{}', lang1 = '{}'".format(self.country, self.lang1))
        print("~" * 80)

window = SimpleWindow()
window.show()
Gtk.main()

combo-box-text.ui as diff:

⟫🥷⟫ diff -u0 combo-box.ui combo-box-text.ui 
--- combo-box.ui	2021-07-18 22:15:11.636047337 +0900
+++ combo-box-text.ui	2021-07-19 00:27:27.848236975 +0900
@@ -81 +81 @@
-          <object class="GtkComboBox" id="combo_country">
+          <object class="GtkComboBoxText" id="combo_country">
@@ -108 +108 @@
-          <object class="GtkComboBox" id="combo_lang1">
+          <object class="GtkComboBoxText" id="combo_lang1">

show() and show_all()

As I see my examples up to here, I casually mix window.show() and window.show_all(). Reading Gtk.Widget class, I realize that show() method is good enough for situation in my previous examples and the recursive show_all() method is the more robust approach which works even for more complex situation.

Previous Post Top Next Post