Überlistet
| Anke (encarsia) | Auch verfügbar in: English
Daten in ListStore speichern und mit ComboBox und TreeView anzeigen
Für die Speicherung und Anzeige von Daten in Listen- oder Tabellenform benötigt man in GTK+-Anwendungen verschiedene Elemente:
-
Im Modell werden die Daten verwaltet, es gibt zwei Typen:
ListStore: flache Liste, die Spalten können neben Text-, Zahlenwerten auch GTK+-Elemente (z.B. Buttons, Checkboxen) enthalten
TreeStore: funktioniert prinzipiell wie ListStore, Zeilen können ihrerseits Kind-Einträge besitzen, Daten können im Gegensatz zu ListStore nicht in Glade angegeben werden (TreeStore-Artikel)
-
Widgets:
TreeView: dieses Widget eignet sich zum Anzeigen, Sortieren, Bearbeiten von Daten, wird von beiden Modelltypen verwendet; es können parallel mehrere TreeView-Widgets angelegt werden, die auf dieselbe Datenbasis (Modell) zurückgreifen, aber zum Beispiel verschiedene Spalten anzeigen
ComboBox: Comboboxen dienen der Auswahl aus einer gegebenen Liste, deren Datenbasis ein List- oder TreeStore sein kann (siehe Artikel zu Spinbutton und Combobox)
CellRenderers: Unterwidgets, in denen die anzuzeigenden Daten, deren Layout und weitere Optionen wie Bearbeitbarkeit festgelegt werden
Glade
ListStore
Um die Vielseitigkeit von ListStore zu skizzieren, wird im Beispiel ein Gtk.ListStore (zu finden in der Elementauswahl links unter "Sonstiges > Listenverwahrung") erstellt und von drei Widgets verwendet.
Zunächst werden ein paar Spalten erstellt. ListStore-Daten lassen sich direkt in Glade eingeben. Dies ist allerdings nur für wenige Zeilen und Spalten praktikabel und übersichtlich. Selbst wenige Daten würde ich immer direkt im Python-Code einlesen.
Wie man sieht, werden Änderungen im ListStore (Sortierung, Inhalt) sofort in allen Widgets aktualisiert, die auf dieses Objekt zugreifen. Für verschiedene Sortierungen des selben List-/TreeStores muss man Gtk.TreeModelSort anwenden (Beispiel siehe TreeStore-Artikel).
Widgets
- ComboBox
-
Als "Baumansichtsmodell" wird wie auch bei den folgenden Widgets der ListStore ausgewählt. Über "Edit > Hierarchie" ein CellRendererText hinzugefügt. Im ersten Feld ("Text") stellt man ein, aus welcher Spalte das Dropdown-Menü angezeigt werden soll. Um die Auswahl zu verarbeiten, wird das Signal changed belegt.
- TreeView #1
-
Das erste TreeView-Widget wird innerhalb eines Gtk.ScrolledWindow-Containers angelegt. Wie bei ComboBox werden nun beliebige CellRenderer angelegt. Wird der Sortierungsanzeiger aktiviert, können die Spalten mit Klick auf den Spaltenkopf sortiert werden. In der Sortierspaltenkennung wird die Spalte angegeben, nach der sortiert werden soll, auf diese Weise kann man eine Spalte auch gemäß einer anderen Spalte sortieren (hier im Beispiel wird die mittlere Spalte nach der letzten sortiert, die Sortierung der beiden hinteren Spalten liefert also das gleiche Ergebnis.
- TreeView #2
-
Das zweite TreeView-Widget wird innerhalb eines Sichtfeldes (Gtk.Viewport) erstellt. Dieser Container bietet keine Scrollbalken, das Widget vergrößert automatisch, so dass alle Zeilen sichtbar sind. Bei größeren Tabellen ist ein ScrolledWindow also praktikabler. Es werden die gleichen Daten angezeigt wie zuvor, allerdings ohne Sortierungsanzeiger, dafür wird die mittlere Spalte ("Description") editierbar gemacht und erhält eine Funktion für das Signal edited.
- Button
-
Ein Klick auf den Button soll jeweils eine weitere Zeile zum ListStore hinzufügen, es wird also das clicked-Signal belegt.
Python
TreeStore
Die in TreeStore vorhandenen Zeilen lassen sich einfach über for row in store
abrufen. Neue Zeilen lassen sich mit append
hinzufügen, andere Optionen wären insert
oder remove
, um Zeilen an bestimmten Positionen einzufügen oder zu entfernen.
ComboBox
Normalerweise benötigt man für den Zugang zu einer Datenzeile einen TreeIter, das Objekt, das auf den Pfad im Modell zeigt (alternativ kann man diese auch über TreePath ansprechen).
iter, model = widget.get_active_iter(), widget.get_model() row = model[iter] print("Selection:", row[0])
Zellen bearbeiten
Das edited-Signal übergibt als Parameter die bearbeitete Zeile und den neuen Zelleninhalt. Dieser muss allerdings explizit als neuer Zelleninhalt übergeben werden, sonst zeigt die Zelle nach der Bearbeitung wieder den alten Inhalt an. Dafür kann man einfach die vom Widget übergebene Position (TreePath) statt des TreeIters verwenden.
def on_cellrenderer_descr_edited(self, widget, pos, edit): x.store[int(pos)][1] = edit
Listings
Python
#!/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()
Glade
<?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">nö</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>
Kommentare
Comments powered by Disqus