Python Forum

Full Version: How to retrieve liststore iter when treeview row selected using sort and filter
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
My code works correctly when I pass a liststore to a treemodelsort which is passed to a treeview. I can select a row in the treeview and delete the corresponding row from the liststore. The problem comes in when I add a filter into the mix.

liststore -> treemodelsort -> filter -> treeview

Everything still displays as desired. The display is correctly sorted and the filter works correctly. However, I cannot figure out how to retrieve an iter to the liststore that corresponds to the selected treeview row. I have spent 2 days trying to get this to work and combed the internet.

My program is too complex to post. So I developed a simple script that demonstrates the problem. When I eliminate the filter in this script, I can delete rows from the liststore. When the filter is in place, I cannot. The script as presented has the filter in place.

I hope I am posting the script correctly. I have read that code should be preceded and followed by 3 back-ticks. I have done this, but the indentations of the code have been lost.

The delete routine, on_butt_delete(), shown works when there is no filter.

All suggestions welcome.

import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk

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

        self.set_default_size(400,400)

        self.grid=Gtk.Grid()
        self.add(self.grid)

        self.ls=Gtk.ListStore(str)

        self.ls.append(['bbb'])
        self.ls.append(['aaa'])
        self.ls.append(['ccc'])

        self.sorted_store = Gtk.TreeModelSort(model=self.ls)
        self.sorted_store.set_sort_column_id(0, Gtk.SortType.ASCENDING)

        self.aFilter = self.sorted_store.filter_new()
        self.aFilter.set_visible_func(self.theFunc)

        self.tv=Gtk.TreeView(self.aFilter)
        self.grid.attach(self.tv, 0, 0, 1, 1)

        self.renderer = Gtk.CellRendererText()
        self.column = Gtk.TreeViewColumn('Title', self.renderer, text=0)
        self.tv.append_column(self.column)


        self.butt=Gtk.Button('Delete bbb')
        self.butt.connect("clicked", self.on_butt_delete)
        self.grid.attach(self.butt, 0, 5, 1, 1)

     def on_butt_delete(self, widget):
        selection = self.tv.get_selection()
        (theStore, theIter) = selection.get_selected()
        theIter = theStore.convert_iter_to_child_iter(theIter)
        self.ls.remove(theIter)

    def theFunc(self, a, b, c):
        return True

win=TreeViewFilterWindow()
win.show_all()
win.connect('destroy', Gtk.main_quit)
Gtk.main()
Thank-you, Larz60+. I could see that the formatting was lost, but nothing I tried after reading what documents I could find worked. I'm going to see if editing my code above will allow me to see what you did. And I will use the bbcode link you provided.Thanks again.
Don't edit your code, create a new post.
I have to believe that many, many coders have encountered the same situation, yet no one has replied. Since I cannot spend more time on this, I will describe my workaround. I have added a column to the liststore which is hidden in the treeview. At startup as the liststore is being loaded with data, I generate a unique serial number which I place in the new column. When it comes time to remove a row, I retrieve this number from the treeview and iterate through the liststore looking for it. I count the rows up to and including the row having the number. I then use get_path(), get_iter() and remove() to remove the doomed row. The routine I use follows.

def delete_item_from_store(self, serial_number):
    # delete currently selected item from store
    store = get_store()
    found = False
    c = -1

    for row in store:
        c += 1

        if row[0] == serial_number:
            found = True
            path = Gtk.TreePath(c)
            the_iter = store.get_iter(path)

            try:
                store.remove(the_iter)
                break
            except Exception as e:
                err(self, True, 'delete_item_from_store()', str(e.args[0]))
                break
        
    if found == False:
        err(self, True, 'serial number not found')
If you want delete the row that contains "bbb"

#!/usr/bin/python3
# -*- coding: utf-8 -*-
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
 
class TreeViewFilterWindow(Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self, title="Treeview Filter Demo")
        self.set_border_width(10)
 
        self.set_default_size(400,400)
 
        self.grid=Gtk.Grid()
        self.add(self.grid)
 
        self.ls=Gtk.ListStore(str)
 
        self.ls.append(['bbb'])
        self.ls.append(['aaa'])
        self.ls.append(['ccc'])
 
        self.sorted_store = Gtk.TreeModelSort(model=self.ls)
        self.sorted_store.set_sort_column_id(0, Gtk.SortType.ASCENDING)
 
        self.aFilter = self.sorted_store.filter_new()
        self.aFilter.set_visible_func(self.theFunc)
 
        self.tv=Gtk.TreeView()
        self.tv.set_model(self.aFilter)
        self.grid.attach(self.tv, 0, 0, 1, 1)
 
        self.renderer = Gtk.CellRendererText()
        self.column = Gtk.TreeViewColumn('Title', self.renderer, text=0)
        self.tv.append_column(self.column)
 
 
        self.butt=Gtk.Button(label='Delete bbb')
        self.butt.connect("clicked", self.on_butt_delete)
        self.grid.attach(self.butt, 0, 5, 1, 1)
 
    def on_butt_delete(self, widget):
        theIter = Gtk.TreeIter()
        theStore = self.ls
        for row in theStore:
            if row[0] == "bbb":
                path = row.path
                theIter = theStore.get_iter(path)
                self.ls.remove(theIter)
 
    def theFunc(self, a, b, c):
        return True
 
win=TreeViewFilterWindow()
win.show_all()
win.connect('destroy', Gtk.main_quit)
Gtk.main()