I am trying to implement a filter for a GtkTreeModel
using a GtkTreeModelFilter
, however, it doesn’t seem to be working as I would expect.
In the example program below, setting any filter will present an empty the GtkTreeView
if the filter applies to nested elements. What I mean by that is if the filter matches on filenames, those rows are correctly returned as visible
. However, the GtkTreeModelFilter
does not automatically mark all parents as visible
. This results in an empty TreeView.
On the other hand, if the filter “.*etc”, then it will display some matches because the top level row is marked as visible
.
If I am reading the documentation for GtkTreeModelFilter
correct, the toggling of parent row visibility is supposed to happen automatically:
Determining the visibility state of a given node based on the state of its child nodes is a frequently occurring use case. Therefore, GtkTreeModelFilter explicitly supports this. For example, when a node does not have any children, you might not want the node to be visible. As soon as the first row is added to the node’s child level (or the last row removed), the node’s visibility should be updated.
Am I doing something wrong in my example or am I misreading the documentation?
Thank you.
#!/usr/bin/python3
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
import os
import re
class MainWindow(Gtk.Window):
def __init__(self):
self.regexp = None
Gtk.Window.__init__(self, title="TreeView Filter Test")
self.grid = Gtk.Grid()
self.add(self.grid)
self.tree_model = Gtk.TreeStore(str)
self.populate_tree("/etc")
self.filter_model = self.tree_model.filter_new()
self.filter_model.set_visible_func(self.apply_filter)
self.tree_view = Gtk.TreeView.new_with_model(self.filter_model)
renderer = Gtk.CellRendererText()
column = Gtk.TreeViewColumn("Filename", renderer, text=0)
self.tree_view.append_column(column)
self.tree_view.expand_all()
self.scrolled_window = Gtk.ScrolledWindow()
self.scrolled_window.set_vexpand(True)
self.scrolled_window.set_hexpand(True)
self.scrolled_window.add(self.tree_view)
self.grid.attach(self.scrolled_window, 0, 0, 2, 1)
self.filter_entry = Gtk.Entry()
self.filter_entry.set_hexpand(True)
self.grid.attach(self.filter_entry, 0, 1, 1, 1)
self.filter_button = Gtk.Button("Filter")
self.filter_button.connect("clicked", self.set_filter)
self.grid.attach(self.filter_button, 1, 1, 1, 1)
self.show_all()
def populate_tree(self, pathname, parent=None):
tree_iter = self.tree_model.append(parent, [os.path.basename(pathname)])
try: entries = os.listdir(pathname)
except OSError: return
for entry in entries:
if os.path.isdir(os.path.join(pathname, entry)):
self.populate_tree(os.path.join(pathname, entry), tree_iter)
elif os.path.isfile(os.path.join(pathname, entry)):
self.tree_model.append(tree_iter, [entry])
def set_filter(self, button):
self.filter = self.filter_entry.get_text()
if not self.filter:
self.regexp = None
else:
self.regexp = re.compile(self.filter)
self.filter_model.refilter()
def apply_filter(self, model, iter, data):
visible = True
if self.regexp:
if not self.regexp.search(model[iter][0]):
visible = False
print("%s -> %u" % (model[iter][0], visible))
return visible
win = MainWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()