[kwlug-disc] Filtering on Two Columns in Gtk+ v3 and Python.

Paul Nijjar paul_nijjar at yahoo.ca
Fri Jun 11 23:25:14 EDT 2021


Okay, I finally took a look at this. 

A Gtk.TreeModelFilter wraps a TreeModel. However, it is itself an
instance of a TreeModel, so you can have one TreeModelFilter use
filter_new() to generate another one. In particular, let's look at the
following snippet from your __init__ function. Once you uncomment your
code (and regroup a little), you have something like:

---

         self.current_filter_language = None
         self.current_filter_year = None

         # Creating the filters, feeding it with the liststore model
         self.language_filter = self.software_liststore.filter_new()
         self.language_filter.set_visible_func(self.language_filter_func)

         self.year_filter = self.software_liststore.filter_new()
         self.year_filter.set_visible_func(self.year_filter_func)

---

The line:

         self.language_filter = self.software_liststore.filter_new()

says "Take the self.software_liststore TreeModel (which in your case is a ListStore, but that is fine because every ListStore is also a TreeModel) and make a TreeModelFilter that wraps that. 

By "stacking" the TreeModelFilters, it means you do not start by wapping the software_liststore, but rather wrapping the existing TreeModelFilter you already made. In particular, the line 


         self.year_filter = self.software_liststore.filter_new()

changes to 

         self.year_filter = self.language_filter.filter_new()

What you have now is the original ListStore (self.software_liststore) being wrapped by your language filter (self.language_filter) wrapped by your year filter (self.year_filter). 


This makes your code work, as far as I can tell. 
The style seems a bit weird -- usually when you duplicate code closely as you do with the year/language filter I get worried -- but looking at the docs maybe this is not avoidable.

- Paul




On Sat, Jun 05, 2021 at 05:13:08AM -0400, John Driezen wrote:
> I am attempting to extend the demo program at
> https://python-gtk-3-tutorial.readthedocs.io/en/latest/treeview.html to
> filter on two columns and sort the resulting filtered rows. The tutorial
> suggests the following: "Instances of Gtk.TreeModelFilter|| can be stacked
> one onto another, to use multiple filters on the same model (in the same way
> you’d use “AND” clauses in a SQL request)."   However the tutorial does not
> give an example of this, and my google fu is failing to find an example
> online.  My attempt at filtering by two columns follows.  The code works
> when filtering by programming language, or when filtering by year.  I don't
> know how to filter by both programming language and year.  Suggestions
> anyone?
> 
> """ Sorted and Filtered TreeView Demo Program. Filters on two columns. """
> import gi
> 
> gi.require_version("Gtk", "3.0")
> from gi.repository import Gtk
> 
> # list of tuples for each software
> SOFTWARE_LIST = [
>     ("Firefox", 2002, "C++"),
>     ("Eclipse", 2004, "Java"),
>     ("Pitivi", 2004, "Python"),
>     ("Netbeans", 1996, "Java"),
>     ("Chrome", 2008, "C++"),
>     ("Filezilla", 2001, "C++"),
>     ("Bazaar", 2005, "Python"),
>     ("Git", 2005, "C"),
>     ("Linux Kernel", 1991, "C"),
>     ("GCC", 1987, "C"),
>     ("Frostwire", 2004, "Java"),
>     ("Homebank", 1995, "C"),
>     ("Tellico", 2001, "C++"),
>     ("gLabels", 2001, "C++"),
>     ("Deluge", 2007, "Python"),
> ]
> 
> class TreeViewFilterWindow(Gtk.Window):
>     """ TreeViewFilterWindow class """
>     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
> positioned
>         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)
>         for software_ref in SOFTWARE_LIST:
>             self.software_liststore.append(list(software_ref))
> 
>         self.current_filter_language = None
>         self.current_filter_year = None
> 
>         # Creating the filters, feeding it with the liststore model
>         self.language_filter = self.software_liststore.filter_new()
>         #self.year_filter = self.software_liststore.filter_new()
>         # Setting the filter functions
> self.language_filter.set_visible_func(self.language_filter_func)
>         #self.year_filter.set_visible_func(self.year_filter_func)
> 
>         # Creating the treeview, using the sorted filter as a model and
> adding the columns
>         self.sortedandfilteredtree =
> Gtk.TreeModelSort(model=self.language_filter)
>         #self.sortedandfilteredtree =
> Gtk.TreeModelSort(model=self.year_filter)
>         self.sortedtreeview =
> Gtk.TreeView.new_with_model(self.sortedandfilteredtree)
>         for i, column_title in enumerate(["Software", "Release Year",
> "Programming Language"]):
>             renderer = Gtk.CellRendererText()
>             column = Gtk.TreeViewColumn(column_title, renderer, text=i)
>             column.set_sort_order(Gtk.SortType.ASCENDING)
>             column.set_sort_column_id(i)
>             self.sortedtreeview.append_column(column)
> 
>         # Creating buttons to filter by programming language and setting up
> their events
>         self.language_buttons = list()
>         for prog_language in ["Java", "C", "C++", "Python", "None"]:
>             button = Gtk.Button(label=prog_language)
>             self.language_buttons.append(button)
>             button.connect("clicked",
> self.on_language_selection_button_clicked)
> 
>         # Creating buttons to filter by release year and setting up their
> events
>         self.year_buttons = list()
>         for year in [1987, 1991, 1995, 1996, 2001, 2002, 2004, 2005, 2007,
> 2008, "None"]:
>             button = Gtk.Button(label=year)
>             self.year_buttons.append(button)
>             button.connect("clicked", self.on_year_selection_button_clicked)
> 
>         # Setting up the layout, putting the treeview in a scrollwindow, and
> the buttons in a
>         # set_row_homogeneous
>         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.language_buttons[0],
> self.scrollable_treelist,
>                                  Gtk.PositionType.BOTTOM, 1, 1)
> 
>         # Layout language selection buttons
>         for i, button in enumerate(self.language_buttons[1:]):
>             self.grid.attach_next_to(button, self.language_buttons[i],
> Gtk.PositionType.RIGHT, 1, 1)
> 
>         self.grid.attach_next_to(self.year_buttons[0],
> self.language_buttons[0],
>                                  Gtk.PositionType.BOTTOM, 1, 1)
> 
>         # Layout year selection buttons
>         for i, button in enumerate(self.year_buttons[1:]):
>             self.grid.attach_next_to(button, self.year_buttons[i],
> Gtk.PositionType.RIGHT, 1, 1)
> 
>         self.scrollable_treelist.add(self.sortedtreeview)
> 
>         self.show_all()
> 
>     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 year_filter_func(self, model, iter, data):
>         """ Tests if the year in the row is the one in the filter. """
>         if self.current_filter_year is None or self.current_filter_year ==
> "None":
>             return True
>         else:
>             return model[iter][1] == self.current_filter_year
> 
>     def on_language_selection_button_clicked(self, widget):
>         """ Called on any of the language button clicks """
>         # Set the current language filter to the button's
> scrollable_treelist
>         self.current_filter_language = widget.get_label()
>         print("%s language selected!" % self.current_filter_language)
>         # Update the filter, which updates the view in turn
>         self.language_filter.refilter()
> 
>     def on_year_selection_button_clicked(self, widget):
>         """ Called on any of the year selection button clicks. """
>         if (widget.get_label() == "None"):
>             self.current_filter_year = None
>         else:
>             self.current_filter_year = int(widget.get_label())
>         print("year %s selected!" % self.current_filter_year)
>         self.year_filter.refilter()
> 
> TV_WIN = TreeViewFilterWindow()
> TV_WIN.connect("destroy", Gtk.main_quit)
> TV_WIN.show_all()
> Gtk.main()
> 

> _______________________________________________
> kwlug-disc mailing list
> kwlug-disc at kwlug.org
> https://kwlug.org/mailman/listinfo/kwlug-disc_kwlug.org


-- 
Events: https://feeds.off-topic.kwlug.org 
Blog: http://pnijjar.freeshell.org




More information about the kwlug-disc mailing list