ListStore

Deutsch   |   Source

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 CellRenderer 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.

System Message: WARNING/2 (<string>, line 45); backlink

Inline emphasis start-string without end-string.
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()
Comments powered by Disqus