Ansichtssache

English   |   Source

Daten anzeigen mit TreeStore

(Fortsetzung zum ListStore-Artikel)

/images/12_treestore.thumbnail.png

TreeStore vs. ListStore

Im Gegensatz zum ListStore können Zeilen eines TreeStores ihrerseits Kind-Elemente besitzen, die append-Funktion benötigt demzufolge ein weiteren Parameter, der einen Bezug zu einer anderen Datenzeile anzeigt:

#append row to liststore
store.append([value1,value2,value3])

#append row to treestore
store.append(parent,[value1,value2,value3])

Der Wert der Variable parent ist entweder

  • None, wenn die Zeile keine übergeordnete Zeile besitzt, oder
  • TreeIter, der zur übergeordneten Zeile zeigt

Der TreeIter wird beim Erstellen einer Zeile erzeugt, untergeordnete Zeilen werden nach folgendem Schema angelegt:

row1 = store.append(None,[value1,value2,value3])
row2 = store.append(row1,[value1,value2,value3])

Man erhält den TreeIter-Wert einer Zeile am einfachsten über die get_selection-Funktion des GtkTreeSelection-Widgets von TreeView (wird automatisch angelegt).

Glade

Im Beispiel werden zwei TreeStores und die jeweils enthaltenen Spalten angelegt, dazu die TreeView-Widgets zur Anzeige.

TreeModelSort

Spalten lassen sich mit der Funktion set_sort_column_id einfach sortieren. Wendet man diese Funktion direkt auf TreeStore an, werden logischerweise alle TreeView-Widgets, die darauf zurückgreifen, sortiert.

Für diese Fälle muss man TreeModelSort-Elemente "zwischenschalten", d.h. man erstellt aus der Widget-Seitenleiste unter "Sonstiges > Sortierung für Baumansichtsmodell" (4. Eintrag) ein Widget und weist ihm den gewünschten TreeStore zu (einzige Option unter "Allgemein"). Anschließend ersetzt man im TreeView das Modell mit dem eben erstellten TreeModelSort.

Die Sortierungsfunktion führt man wie zuvor, nur auf das TreeModelSort-Objekt, aus.

TreeModelFilter

TreeModelFilter ermöglicht die Darstellung bestimmter Zeilen, in Glade wird wie bei TreeModelSort verfahren, zuerst das Element anlegen (3. Eintrag unter "Sonstige"), anschließend erfolgen die Zuweisungen zum Modell und TreeView.

Im gewählten Beispiel sollen Sorten nach der Fruchtfarbe sortiert werden, es wird also noch ein Container für Buttons benötigt, also eine GtkButtonBox.

Formatierung aus dem Modell laden

Neben den anzuzeigenden Spalten gibt es im ersten TreeStore eine Spalte "weight". Der Wert in dieser Spalte wird dazu verwendet, die Zelle in Fettschrift darzustellen. Dazu wird in den Eigenschaften des CellRenderers unter Schriftgewicht die entsprechende Spalte angegeben (der Wert für normale Schrift ist 400). Analog dazu können beispielsweise auch Zellen eingefärbt oder weitere Schriftformatierungen vorgenommen werden.

Python

TreeModelSort

Durch die Positionsabfrage von GtkTreeSelection.get_selected() erhält man ein Tupel (model,pos), pos von model zeigt dabei auf TreeModelSort (bzw. analog auf TreeModelFilter), nicht auf TreeStore und erfordert eine Konvertierung:

model,pos = selection.get_selected()
converted_iter = treesort.convert_iter_to_child_iter(pos)
store.set_value(converted_iter,column,value)

TreeModelFilter

Zunächst muss eine Filterfunktion erstellt werden, in der die Sichtbarkeit von Zeilen definiert wird, im Beispiel also die Variable self.color:

def color_filter_func(self,model,iter,data):
    if model[iter][2] == self.color:
        return True
    else:
        return False

Die Funktion wird zunächst nach dem Schema

treefilter.set_visible_func(filter_func)

zugewiesen, jede Filterung wird dann per refilter() ausgelöst, also wenn das Button-Signal ausgelöst wird:

def on_button_clicked(self,widget):
    x.color = widget.get_label()
    x.obj("treefilter").refilter()

Listings

Glade

12_treestore.glade (Source)

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
  <requires lib="gtk+" version="3.20"/>
  <object class="GtkTreeStore" id="filterstore">
    <columns>
      <!-- column-name var -->
      <column type="gchararray"/>
      <!-- column-name spec -->
      <column type="gchararray"/>
      <!-- column-name color -->
      <column type="gchararray"/>
    </columns>
  </object>
  <object class="GtkTreeModelFilter" id="treefilter">
    <property name="child_model">filterstore</property>
  </object>
  <object class="GtkTreeStore" id="store">
    <columns>
      <!-- column-name name -->
      <column type="gchararray"/>
      <!-- column-name spec -->
      <column type="gchararray"/>
      <!-- column-name color -->
      <column type="gchararray"/>
      <!-- column-name note -->
      <column type="gchararray"/>
      <!-- column-name weight -->
      <column type="gint"/>
      <!-- column-name counter -->
      <column type="gint"/>
    </columns>
  </object>
  <object class="GtkTreeModelSort" id="treesort">
    <property name="model">store</property>
  </object>
  <object class="GtkApplicationWindow" id="window">
    <property name="width_request">600</property>
    <property name="height_request">500</property>
    <property name="can_focus">False</property>
    <property name="title" translatable="yes">Titel</property>
    <signal name="destroy" handler="on_window_destroy" swapped="no"/>
    <child>
      <object class="GtkBox" id="box">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="orientation">vertical</property>
        <property name="spacing">12</property>
        <property name="homogeneous">True</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" id="view">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="model">treesort</property>
                <property name="headers_clickable">False</property>
                <property name="rules_hint">True</property>
                <child internal-child="selection">
                  <object class="GtkTreeSelection" id="selection"/>
                </child>
                <child>
                  <object class="GtkTreeViewColumn">
                    <property name="title" translatable="yes">Name</property>
                    <property name="sort_order">descending</property>
                    <property name="sort_column_id">5</property>
                    <child>
                      <object class="GtkCellRendererText"/>
                      <attributes>
                        <attribute name="text">0</attribute>
                        <attribute name="weight">4</attribute>
                      </attributes>
                    </child>
                  </object>
                </child>
                <child>
                  <object class="GtkTreeViewColumn">
                    <property name="title" translatable="yes">Farbe</property>
                    <child>
                      <object class="GtkCellRendererText"/>
                      <attributes>
                        <attribute name="text">2</attribute>
                      </attributes>
                    </child>
                  </object>
                </child>
                <child>
                  <object class="GtkTreeViewColumn">
                    <property name="title" translatable="yes">Bemerkung</property>
                    <child>
                      <object class="GtkCellRendererText" id="cellrenderer_note">
                        <property name="editable">True</property>
                        <signal name="edited" handler="on_cellrenderer_note_edited" swapped="no"/>
                      </object>
                      <attributes>
                        <attribute name="text">3</attribute>
                      </attributes>
                    </child>
                  </object>
                </child>
              </object>
            </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="visible">True</property>
            <property name="can_focus">False</property>
            <child>
              <object class="GtkScrolledWindow">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="shadow_type">in</property>
                <property name="propagate_natural_width">True</property>
                <child>
                  <object class="GtkTreeView">
                    <property name="visible">True</property>
                    <property name="can_focus">True</property>
                    <property name="model">treefilter</property>
                    <child internal-child="selection">
                      <object class="GtkTreeSelection"/>
                    </child>
                    <child>
                      <object class="GtkTreeViewColumn">
                        <property name="title" translatable="yes">Sorte</property>
                        <child>
                          <object class="GtkCellRendererText"/>
                          <attributes>
                            <attribute name="text">0</attribute>
                          </attributes>
                        </child>
                      </object>
                    </child>
                    <child>
                      <object class="GtkTreeViewColumn">
                        <property name="title" translatable="yes">Art</property>
                        <child>
                          <object class="GtkCellRendererText"/>
                          <attributes>
                            <attribute name="text">1</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="GtkButtonBox" id="buttonbox">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="orientation">vertical</property>
                <property name="layout_style">start</property>
                <child>
                  <placeholder/>
                </child>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="padding">5</property>
                <property name="pack_type">end</property>
                <property name="position">1</property>
              </packing>
            </child>
          </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

12_treestore.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_button_clicked(self,widget):
        x.color = widget.get_label()
        x.obj("treefilter").refilter()

    def on_cellrenderer_note_edited(self,widget,row,edit):
        model,pos = x.obj("selection").get_selected()
        conv_iter = x.obj("treesort").convert_iter_to_child_iter(pos)
        x.obj("store").set_value(conv_iter,3,edit)

class Example:

    def __init__(self):

        self.builder = Gtk.Builder()
        self.builder.add_from_file("12_treestore.glade")
        self.builder.connect_signals(Handler())
        self.obj = self.builder.get_object

        #read data from text file
        with open("12_capsicum.txt") as f:
            data = [line.strip("\n") for line in f]

        colors = set()
        species = set()
        varieties = []
        for line in data:
            variety = line.split(";")
            colors.add(variety[2])
            species.add(variety[1])
            try:
                variety[3]
            except IndexError:
                variety.append("")
            varieties.append(variety)

        #append lines to 1st treestore
        for s in species:
            counter = 0
            row = self.obj("store").append(None,[None,None,None,None,800,None])
            for v in varieties:
                if v[1] == s:
                    self.obj("store").append(row,[v[0],v[1],v[2],v[3],400,None])
                    counter += 1
            self.obj("store").set_value(row,0,"%s (%d)" % (s,counter))
            self.obj("store").set_value(row,5,counter)

        #append lines to 2nd treestore
        [self.obj("filterstore").append(None,[v[0],v[1],v[2]]) for v in varieties]

        #create buttons in buttonbox
        for c in colors:
            button = Gtk.Button.new_with_label(c)
            button.connect("clicked",Handler().on_button_clicked)
            self.obj("buttonbox").add(button)

        self.obj("view").expand_all()
        self.obj("treesort").set_sort_column_id(5, Gtk.SortType.DESCENDING)
        self.obj("treefilter").set_visible_func(self.color_filter_func)
        self.obj("window").show_all()

    def color_filter_func(self,model,iter,data):
        if model[iter][2] == self.color:
            return True
        else:
            return False

    def main(self):
        Gtk.main()

x = Example()
x.main()
Comments powered by Disqus