Objekt-Attribute in Gtk.TreeView anzeigen¶
Wenn man in Python und PyGObject mit Gtk.TreeView
arbeitet,
ist es oft umständlich, wenn man Daten, die man als Attribute von
Objekten gespeichert hat, erst auf die Spalten eines
Gtk.ListStore
oder Gtk.TreeStore
verteilen muss, um
sie im TreeView darstellen zu können.
Praktischer ist es oft, wenn der TreeView direkt Objekt-Attribute für die einzelnen Spalten benutzen kann.
Das geht allerdings nur, wenn
unser Objekt zu einer Unterklasse von GObject.GObject
gehört. Und es ist deutlich langsamer, da der Zugriff auf die
Objekt-Attribute über Python-Code statt nativ in Gtk erfolgt. Bei
TreeViews mit sehr vielen Einträgen kann der
Geschwindigkeitsunterschied für die Anwenderin spürbar sein, besonders
beim Sortieren.
Ähnlich kann man natürlich auch auf die Felder eines dict
zugreifen, oder berechnete Werte anzeigen.
Objekt-Attribute darstellen¶
Gtk.TreeViewColumn
hat eine Methode
set_cell_data_func()
. Damit können Sie selbst eine Funktion
angeben, die die Spalte benutzen soll, um sich vom zugehörigen Modell
die Daten zu beschaffen. Dafür benötigt set_cell_data_func()
als Parameter den Gtk.CellRenderer
, der für die Darstellung
der Spalte zuständig ist, die gewünschte Funktion und bei Bedarf
zusätzliche Daten für die Funktion:
treeviewcolumn.set_cell_data_func(cellrenderer, cell_data_func, data)
Die Funktion muss vom Typ Gtk.TreeCellDataFunc
sein und wird
von Gtk.TreeViewColumn
mit fünf Parametern aufgerufen: Der
TreeViewColumn, dem für die Darstellung der Spalte zuständigen
CellRenderer, dem zugehörigen Modell, einem Iter, der auf die
aktuelle Zeile zeigt, und den zusätzlichen Daten, die mit
set_cell_data_func übergeben wurden:
def cell_data_func(tree_column, cell, tree_model, iter, data):
Nehmen wir an, unser Objekt steht in der ersten Spalte des Modells, und hat
ein Attribut title, das wir in einer Spalte anzeigen wollen, die von einem
Gtk.CellRendererText
dargestellt wird.
Zuerst besorgen wir uns aus dem Modell das Objekt für die aktuelle Zeile:
obj = tree_model[iter][0]
Dann setzen wir die Eigenschaft text des CellRenderers auf den Wert des title-Attributes unseres Objekts:
cell.set_property('text', obj.title)
Insgesamt sieht unsere Funktion also so aus:
def my_cell_data_func(tree_column, cell, tree_model, iter, data):
obj = tree_model[iter][0]
cell.set_property('text', obj.title)
Ein komplettes Programm mit Spalten für zwei Attribute kann dann z.B. so aussehen:
1#!/usr/bin/python3
2# -*- coding: utf-8 -*-
3
4from gi.repository import Gtk, GObject
5
6
7class Item(GObject.GObject):
8 """ Eine Klasse für unsere Daten"""
9 def __init__(self, title, num):
10 GObject.GObject.__init__(self)
11 self.title = title
12 self.num = num
13
14
15def my_title_cell_data_func(tree_column, cell, tree_model, iter, data):
16 """ Eine eigene cell_data_func für Item.title"""
17 obj = tree_model[iter][0] # Objekt für aktuelle Zeile
18 cell.set_property('text', obj.title)
19
20def my_num_cell_data_func(tree_column, cell, tree_model, iter, data):
21 """ Eine eigene cell_data_func für Item.num"""
22 obj = tree_model[iter][0] # Objekt für aktuelle Zeile
23 cell.set_property('text', str(obj.num))
24
25
26# Fenster erzeugen
27window = Gtk.Window()
28window.set_default_size(200, 250)
29window.connect("destroy", lambda w: Gtk.main_quit())
30
31# TreeView mit ListStore ins Fenster einfügen
32lstore = Gtk.ListStore(Item)
33treeview = Gtk.TreeView(model=lstore)
34window.add(treeview)
35
36
37## Titel-Spalte erzeugen:
38
39cellrenderer = Gtk.CellRendererText()
40treeviewcolumn = Gtk.TreeViewColumn('Titel')
41
42# benutze my_title_cell_data_func
43treeviewcolumn.set_cell_data_func(cellrenderer, my_title_cell_data_func)
44
45# CellRenderer zur Spalte, und Spalte zum TreeView hinzufügen
46treeviewcolumn.pack_start(cellrenderer, True)
47treeview.append_column(treeviewcolumn)
48
49
50## Num-Spalte erzeugen:
51
52cellrenderer = Gtk.CellRendererText()
53treeviewcolumn = Gtk.TreeViewColumn('Num')
54
55# benutze my_num_cell_data_func
56treeviewcolumn.set_cell_data_func(cellrenderer, my_num_cell_data_func)
57
58# CellRenderer zur Spalte, und Spalte zum TreeView hinzufügen
59treeviewcolumn.pack_start(cellrenderer, True)
60treeview.append_column(treeviewcolumn)
61
62
63
64# Ein paar Demo-Objekte ins Modell packen
65for i in range(19):
66 lstore.append((Item('This is {}'.format(i), i*i), ))
67
68
69# Alles anzeigen
70window.show_all()
71Gtk.main()
Sortieren¶
Damit die Anwenderin die Spalten durch einen Klick auf die
Spaltenköpfe sortieren kann, müssen wir mit
Gtk.TreeViewColumn.set_sort_column_id()
eine Sortier-ID für
die Spalte setzen. Damit das Sortieren auch mit unseren Objekt-Attributen
funktioniert, müssen wir zusätzlich dem Modell unseres TreeViews eine
Vergleichsfunktion für diese ID mitgeben.
Die Sortierfunktion muss vom Typ Gtk.TreeIterCompareFunc sein:
def sort_func(model, iter_a, iter_b, data):
Als model bekommt sie das Modell des TreeView übergeben, iter_a und iter_b sind Iter zu den beiden Objekten, die verglichen werden sollen, und in data stehen eventuell zusätzliche Daten. Ihre Rückgabewerte sind:
-1, falls a < b
0, falls a = b
1, falls a > b
Um unsere Objekte nach dem Titel zu sortieren, können wir beispielsweise folgende Funktion verwenden:
def my_title_sort_func(model, iter_a, iter_b, data):
a = model[iter_a][0]
b = model[Iter_b][0]
return (a.title > b.title) - (a.title < b.title)
Jetzt müssen wir diese Funktion noch dem Modell unseres TreeView mitteilen.
Dafür gibt es die Methode Gtk.TreeSortable.set_sort_func()
:
model.set_sort_func(sort_column_id, sort_func, user_data=None)
Dabei ist sort_column_id die ID, sort_func eine Vergleichsfunktion, und mit user_data können wir bei Bedarf zusätzliche Daten für die Vergleichsfunktion angeben.
Wenn treeviewcolumn eine Spalte ist und model das Modell unseres TreeView, könne wir mit:
treeviewcolumn.set_sort_column_id(0)
model.set_sort_func(0, my_title_sort_func)
die Funktion my_title_sort_func als Sortier-Funktion für die Sortier-ID 0 setzen.
Das komplette Programm, wieder mit Spalten für zwei Attribute, kann dann z.B. so aussehen:
1#!/usr/bin/python3
2# -*- coding: utf-8 -*-
3
4from gi.repository import Gtk, GObject
5
6def cmp(a, b):
7 return (a > b) - (a < b)
8
9class Item(GObject.GObject):
10 """ Eine Klasse für unsere Daten"""
11 def __init__(self, title, num):
12 GObject.GObject.__init__(self)
13 self.title = title
14 self.num = num
15
16
17def my_title_cell_data_func(tree_column, cell, tree_model, iter, data):
18 """ Eine eigene cell_data_func für Item.title"""
19 obj = tree_model[iter][0] # Objekt für aktuelle Zeile
20 cell.set_property('text', obj.title)
21
22def my_title_sort_func(model, a, b, data):
23 obj_a = model[a][0]
24 obj_b = model[b][0]
25 return cmp(obj_a.title, obj_b.title)
26
27
28def my_num_cell_data_func(tree_column, cell, tree_model, iter, data):
29 """ Eine eigene cell_data_func für Item.num"""
30 obj = tree_model[iter][0] # Objekt für aktuelle Zeile
31 cell.set_property('text', str(obj.num))
32
33def my_num_sort_func(model, a, b, data):
34 obj_a = model[a][0]
35 obj_b = model[b][0]
36 return cmp(obj_a.num, obj_b.num)
37
38
39
40# Fenster erzeugen
41window = Gtk.Window()
42window.set_default_size(200, 250)
43window.connect("destroy", lambda w: Gtk.main_quit())
44
45# TreeView mit ListStore ins Fenster einfügen
46lstore = Gtk.ListStore(Item)
47treeview = Gtk.TreeView(model=lstore)
48window.add(treeview)
49
50
51## Titel-Spalte erzeugen:
52
53cellrenderer = Gtk.CellRendererText()
54treeviewcolumn = Gtk.TreeViewColumn('Titel')
55
56# benutze my_title_cell_data_func
57treeviewcolumn.set_cell_data_func(cellrenderer, my_title_cell_data_func)
58
59# CellRenderer zur Spalte, und Spalte zum TreeView hinzufügen
60treeviewcolumn.pack_start(cellrenderer, True)
61treeview.append_column(treeviewcolumn)
62
63# Sortierfunktion setzen
64treeviewcolumn.set_sort_column_id(0)
65lstore.set_sort_func(0, my_title_sort_func)
66
67
68## Num-Spalte erzeugen:
69
70cellrenderer = Gtk.CellRendererText()
71treeviewcolumn = Gtk.TreeViewColumn('Num')
72
73# benutze my_num_cell_data_func
74treeviewcolumn.set_cell_data_func(cellrenderer, my_num_cell_data_func)
75
76# CellRenderer zur Spalte, und Spalte zum TreeView hinzufügen
77treeviewcolumn.pack_start(cellrenderer, True)
78treeview.append_column(treeviewcolumn)
79
80# Sortierfunktion setzen
81treeviewcolumn.set_sort_column_id(1)
82lstore.set_sort_func(1, my_num_sort_func)
83
84
85# Ein paar Demo-Objekte ins Modell packen
86for i in range(19):
87 lstore.append((Item('This is {}'.format(i), i*i), ))
88
89
90# Alles anzeigen
91window.show_all()
92Gtk.main()
Suchen¶
Damit auch die Suche im TreeView funktioniert, müssen wir mit
Gtk.TreeView.set_search_equal_func()
eine Funktion angeben,
die für die Vergleiche bei der Suche benutzt wird. Das ist auch oft
bei “normalen” TreeViews sinnvoll, um zum Beispiel eine Suche nach
Teilstrings zu ermöglichen.
Eine Suchfunktion für Teilstrings kann zum Beispiel so aussehen:
def my_title_search_func(model, column, value, iter):
obj = model[iter][0]
return value not in obj.title
Dabei ist model wie üblich das Model des TreeViews, column gibt die Spalte an, in der gesucht wird, value ist der Wert, nach dem die Benutzerin sucht, und iter der Iter zu der du vergleichenden Zeile im Model. Die Funktion gibt True zurück, wenn der Eintrag nicht passt (der TreeView also weitersuchen muss), und False, falls der Eintrag passt.
Damit der TreeView die Suchfunktion aktiviert, müssen wir noch eine Suchspalte setzen:
treeview.set_search_column(0)
Der Parameter ist dabei eigentlich egal, er wird nur als column an
my_title_search_func()
weitergegeben. Das könnte man zum
Beispiel benutzen, um das für die Suche verwendete Objekt-Attribut zu
ändern.
Das komplette Programm, wieder mit Spalten für zwei Attribute, kann dann z.B. so aussehen:
1#!/usr/bin/python3
2# -*- coding: utf-8 -*-
3
4from gi.repository import Gtk, GObject
5
6def cmp(a, b): # Praktisch für die Sortier-Funktionen
7 return (a > b) - (a < b)
8
9class Item(GObject.GObject):
10 """ Eine Klasse für unsere Daten"""
11
12 def __init__(self, title, num):
13 GObject.GObject.__init__(self)
14 self.title = title
15 self.num = num
16
17
18def my_title_cell_data_func(tree_column, cell, tree_model, iter, data):
19 """ cell_data_func für Item.title"""
20 obj = tree_model[iter][0] # Objekt für aktuelle Zeile
21 cell.set_property('text', obj.title)
22
23def my_title_sort_func(model, a, b, data):
24 """ Sortier-Funktion für Item.title"""
25 obj_a = model[a][0]
26 obj_b = model[b][0]
27 return cmp(obj_a.title, obj_b.title)
28
29def my_title_search_func(model, column, key, iter):
30 """ Such-Funktion für Item.title"""
31 obj = model[iter][0]
32 return key not in obj.title
33
34
35def my_num_cell_data_func(tree_column, cell, tree_model, iter, data):
36 """ cell_data_func für Item.num"""
37 obj = tree_model[iter][0] # Objekt für aktuelle Zeile
38 cell.set_property('text', str(obj.num))
39
40def my_num_sort_func(model, a, b, data):
41 """ Sortier-Funktion für Item.num"""
42 obj_a = model[a][0]
43 obj_b = model[b][0]
44 return cmp(obj_a.num, obj_b.num)
45
46
47
48## Fenster erzeugen
49window = Gtk.Window()
50window.set_default_size(200, 250)
51window.connect("destroy", lambda w: Gtk.main_quit())
52
53## TreeView mit ListStore ins Fenster einfügen
54lstore = Gtk.ListStore(Item)
55treeview = Gtk.TreeView(model=lstore)
56window.add(treeview)
57
58
59## Titel-Spalte erzeugen:
60
61cellrenderer = Gtk.CellRendererText()
62treeviewcolumn = Gtk.TreeViewColumn('Titel')
63
64# benutze my_title_cell_data_func
65treeviewcolumn.set_cell_data_func(cellrenderer, my_title_cell_data_func)
66
67# CellRenderer zur Spalte, und Spalte zum TreeView hinzufügen
68treeviewcolumn.pack_start(cellrenderer, True)
69treeview.append_column(treeviewcolumn)
70
71# Sortierfunktion setzen
72treeviewcolumn.set_sort_column_id(0)
73lstore.set_sort_func(0, my_title_sort_func)
74
75# Suchfunktion setzen
76treeview.set_search_equal_func(my_title_search_func)
77treeview.set_search_column(0)
78
79
80## Num-Spalte erzeugen:
81
82cellrenderer = Gtk.CellRendererText()
83treeviewcolumn = Gtk.TreeViewColumn('Num')
84
85# benutze my_num_cell_data_func
86treeviewcolumn.set_cell_data_func(cellrenderer, my_num_cell_data_func)
87
88# CellRenderer zur Spalte, und Spalte zum TreeView hinzufügen
89treeviewcolumn.pack_start(cellrenderer, True)
90treeview.append_column(treeviewcolumn)
91
92# Sortierfunktion setzen
93treeviewcolumn.set_sort_column_id(1)
94lstore.set_sort_func(1, my_num_sort_func)
95
96
97## Ein paar Demo-Objekte ins Modell packen
98for i in range(19):
99 lstore.append((Item('This is {}'.format(i), i*i), ))
100
101
102# Alles anzeigen
103window.show_all()
104Gtk.main()