Python Forum
How to retrieve liststore iter when treeview row selected using sort and filter
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
How to retrieve liststore iter when treeview row selected using sort and filter
#1
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()
Larz60+ write Jan-20-2021, 06:26 PM:
Please post all code, output and errors (it it's entirety) between their respective tags. Refer to BBCode help topic on how to post. Use the "Preview Post" button to make sure the code is presented as you expect before hitting the "Post Reply/Thread" button.
Fixed for you this time. Please use bbcode tags going forward.
Reply
#2
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.
Reply
#3
Don't edit your code, create a new post.
Reply
#4
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')
Reply
#5
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()
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  [Tkinter] [split] Is there a way to embed a treeview as a row inside another treeview? CyKlop 5 3,346 Oct-20-2021, 12:14 AM
Last Post: CyKlop
  Treeview scroll selected node to top rfresh737 1 2,699 Apr-14-2021, 03:27 AM
Last Post: deanhystad

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020