过滤模型中的 Pygobjects RenderToggle 选择了错误的列

Pygobjects RenderToggle in filtered model selects wrong column

我正在尝试在 pygobjects 的过滤模型中添加一个切换字段。我使用了教程中的代码并进行了增强以添加切换渲染器,但是只要我在过滤列表中单击,切换就会呈现错误。

假设您有一个未过滤的列表:

###########################
# 0|test|a|toggle=False
# 1|test|b|toggle=False
# 2|test|a|toggle=False
# 3|test|a|toggle=False
##########################

当您现在应用过滤器,说您只想查看 b 的项目时,您会得到:

# 1|test|b|toggle=False

如果您现在单击该列,则未筛选列表中的列为 "toggled",因为路径为 0,但应为 1:

# 0|test|a|toggle=True

但我想要的当然是:

# 1|test|b|toggle=True

那我该怎么做呢?

这是我的代码,大部分是从 http://python-gtk-3-tutorial.readthedocs.org/en/latest/treeview.html#filtering 上的教程复制而来的:

from gi.repository import Gtk

#list of tuples for each software, containing the software name, initial release, and main programming languages used
software_list = [("Firefox", 2002,  "C++", False),
                 ("Eclipse", 2004, "Java", False),
                 ("Pitivi", 2004, "Python", False),
                 ("Netbeans", 1996, "Java", False),
                 ("Chrome", 2008, "C++", False),
                 ("Filezilla", 2001, "C++", False),
                 ("Bazaar", 2005, "Python", False),
                 ("Git", 2005, "C", False),
                 ("Linux Kernel", 1991, "C", False),
                 ("GCC", 1987, "C", False),
                 ("Frostwire", 2004, "Java", False)]

class TreeViewFilterWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="Treeview Filter Demo")
        self.set_border_width(10)

        #Setting up the self.grid in which the elements are to be positionned
        self.grid = Gtk.Grid()
        self.grid.set_column_homogeneous(True)
        self.grid.set_row_homogeneous(True)
        self.add(self.grid)

        #Creating the ListStore model
        self.software_liststore = Gtk.ListStore(str, int, str, bool)
        for software_ref in software_list:
            self.software_liststore.append(list(software_ref))
        self.current_filter_language = None

        #Creating the filter, feeding it with the liststore model
        self.language_filter = self.software_liststore.filter_new()
        #setting the filter function, note that we're not using the
        self.language_filter.set_visible_func(self.language_filter_func)

        #creating the treeview, making it use the filter as a model, and adding the columns
        self.treeview = Gtk.TreeView.new_with_model(self.language_filter)
        for i, column_title in enumerate(["Software", "Release Year", "Programming Language"]):
            renderer = Gtk.CellRendererText()
            column = Gtk.TreeViewColumn(column_title, renderer, text=i)
            self.treeview.append_column(column)
        renderer_toggle = Gtk.CellRendererToggle()
        renderer_toggle.connect("toggled", self.on_cell_toggled)
        column_toggle = Gtk.TreeViewColumn("toggle", renderer_toggle, active=3)
        self.treeview.append_column(column_toggle)

        #creating buttons to filter by programming language, and setting up their events
        self.buttons = list()
        for prog_language in ["Java", "C", "C++", "Python", "None"]:
            button = Gtk.Button(prog_language)
            self.buttons.append(button)
            button.connect("clicked", self.on_selection_button_clicked)

        #setting up the layout, putting the treeview in a scrollwindow, and the buttons in a row
        self.scrollable_treelist = Gtk.ScrolledWindow()
        self.scrollable_treelist.set_vexpand(True)
        self.grid.attach(self.scrollable_treelist, 0, 0, 8, 10)
        self.grid.attach_next_to(self.buttons[0], self.scrollable_treelist, Gtk.PositionType.BOTTOM, 1, 1)
        for i, button in enumerate(self.buttons[1:]):
            self.grid.attach_next_to(button, self.buttons[i], Gtk.PositionType.RIGHT, 1, 1)
        self.scrollable_treelist.add(self.treeview)

        self.show_all()
    def on_cell_toggled(self, widget, path):
        self.software_liststore[path][3] = not self.software_liststore[path][3]

    def language_filter_func(self, model, iter, data):
        """Tests if the language in the row is the one in the filter"""
        if self.current_filter_language is None or self.current_filter_language == "None":
            return True
        else:
            return model[iter][2] == self.current_filter_language

    def on_selection_button_clicked(self, widget):
        """Called on any of the button clicks"""
        #we set the current language filter to the button's label
        self.current_filter_language = widget.get_label()
        print("%s language selected!" % self.current_filter_language)
        #we update the filter, which updates in turn the view
        self.language_filter.refilter()


win = TreeViewFilterWindow()
win.connect("delete-event", Gtk.main_quit)
win.show_all()
Gtk.main()

on_cell_toggled() 中的 path 过滤模型 中的路径,因为这是应用于 GtkTreeView 的模型。在更改之前,您需要将其转换为未过滤模型中的路径。为此,请使用过滤模型的 convert_path_to_child_path() 方法。

如果您稍后添加排序,您将需要对已排序模型执行相同的操作,并且对于已排序模型和过滤模型,您将需要调用这两种方法。无论您在原始模型之上放置多少层,您的渲染器的事件处理程序将始终为您提供 GtkTreeView 知道的层的路径。

相同的规则也适用于操纵树视图选择。