I've implemented a custom TreeModel in PyGI (GTK3) the following way (as suggested here and here):
test.py
from gi.repository import Gtk
class Store(GObject.Object, Gtk.TreeModel):
def __init__(self):
self.data = [[i*j for i in range(10)] for j in range(10)] #multiplication table
super(Store, self).__init__()
#boilerplate TreeModel interface implementation
def do_get_flags(self):
print "do_get_flags called"
return Gtk.TreeModelFlags.LIST_ONLY
def do_get_n_columns(self):
print "do_get_n_columns called"
return len(self.data[0])
def do_get_column_type(self, index):
print "do_get_column_type called; index = %s"%(index)
if index < 10:
return str
else:
raise IndexError
def do_get_iter(self, path):
print "do_get_iter called; path = %s"%(path)
indices = path.get_indices()
if indices[0] < len(self.alignment.sequences):
iterator = Gtk.TreeIter()
iterator.user_data = indices[0]
print "iterator.user_data = %s, iterator = %s" % (iterator.user_data, iterator)
return (True, iterator)
else:
return (False, None)
def do_get_path(self, iterator):
print "do_get_path called; iter = %s" % (iter)
if iterator.user_data is not None:
path = Gtk.TreePath(iterator.user_data)
return path
else:
return None
def do_get_value(self, iterator, column_index):
print "do_get_value called; iterator = %s, column_index = %s"%(iterator, column_index)
item = self.data[iterator.user_data][column_index]
def do_iter_next(self, iterator):
print "do_iter_next_called; iterator = %s"%(iterator)
#returns next iterator
if not hasattr(iterator, "user_data"):
print self.do_get_path(iterator)
return
else:
print "user data = %s" % (iterator.user_data)
try:
return self.do_get_iter(Gtk.TreePath((iterator.user_data+1,)))
except IndexError:
return None
def do_iter_has_child(self, iterator):
print "do_iter_has_child called; rowref = %s" % (iterator)
return False
def do_iter_nth_child(self, iterator, index):
print "do_iter_nth_child called; iterator = %s, index = %s" % (iterator, index)
output_iterator = Gtk.TreeIter()
output_iterator.user_data = index
return (True, output_iterator)
def do_iter_parent(self, child):
print "do_iter_parent called; child = %s" % (child)
return None
def main():
model = Store()
view = Gtk.TreeView(model)
for index, column in enumerate(alignment):
renderer = Gtk.CellRendererText()
column = Gtk.TreeViewColumn(str(index))
column.pack_start(renderer, False)
column.add_attribute(renderer, "markup", index)
#column.props.sizing = Gtk.TreeViewColumnSizing.FIXED
view.append_column(column)
scrolled = Gtk.ScrolledWindow()
scrolled.add(view)
window = Gtk.Window(title="title")
window.set_size_request(480, 640)
window.add(scrolled)
window.show_all()
return window
if __name__ == "__main__":
window = main()
window.connect('destroy', Gtk.main_quit)
Gtk.main()
Unfortunately this implementation doesn't work due to crazy behaviour of do_iter_next()
. Look at the contents of logfile, created by python test.py &> test.log
:
test.log
do_get_n_columns called
do_get_column_type called; index = 0
do_get_flags called
do_get_iter called; path = 0
iterator.user_data = 0, iterator = <GtkTreeIter at 0x8797cb0>
do_iter_next_called; iterator = <GtkTreeIter at 0xbfb243c0>
user data = None
Traceback (most recent call last):
File "minimal_example.py", line 65, in do_iter_next
return self.do_get_iter(Gtk.TreePath((iterator.user_data+1,)))
TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'
do_iter_next_called; iterator = <GtkTreeIter at 0xbfb243c0>
user data = None
Traceback (most recent call last):
File "minimal_example.py", line 65, in do_iter_next
return self.do_get_iter(Gtk.TreePath((iterator.user_data+1,)))
TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'
do_iter_next_called; iterator = <GtkTreeIter at 0xbfb243c0>
user data = None
Do you have any ideas about why first do_get_iter() returns one iterator and then do_iter_next() takes on input another one, with empty user_data. Also, do you understand, whether user_data
is a specific field, allotted by GTK3 authors, or not? Reference manual doesn't mention it, but why then it is present for my misterious iter at 0xbfb243c0?
Usage of Gtk.TreeIters.user_data is ok. There was some historical baggage associated with the field in versions of pygobject prior to 3.8 in regards to memory leakage (but it still worked). pygobject 3.8 changed the field to only allow integer values and fixed the leak. However, versions 3.8.0 and 3.8.1 continued to interpret an integer value of 0 as None when accessed which is the problem you are seeing. Versions 3.8.2 and above fixed this so a value of 0 is returned as 0 when accessed.
To support pygobject versions prior to 3.8.2, you can write an accessor function to retrieve the user data:
def get_user_data(tree_iter):
if tree_iter.user_data is None:
return 0
return tree_iter.user_data