Python: Tkinter Treeview Searchable

后端 未结 3 1877
一个人的身影
一个人的身影 2021-02-06 16:21

Fairly straight forward question, and despite my best Google-Fu I can\'t find anything on this.

I have a Python app that uses a Tkinter Treeview widget as a table. This

3条回答
  •  忘了有多久
    2021-02-06 17:07

    The feature you are looking for does not exists out of the box, but you can easily code it.

    In short: subclass Treeview widget, when a key is pressed on your widget, display an entry in the top right corner (place allow you to superimpose widgets), when you are done, remove the entry.

    Here are a few snippets:

    1) subclass the widget you want to extend

    Instantiate your additionals widgets and your bindings.

    class SearchableTreeview(ttk.Treeview):
        def __init__(self, *args, **kwargs):
            ttk.Treeview.__init__(self, *args, **kwargs)
            #create the entry on init but does no show it
            self._toSearch = tk.StringVar()
            self.entry = tk.Entry(self, textvariable=self._toSearch)
    
            self.bind("", self._keyOnTree)
            self._toSearch.trace_variable("w", self._search)
            self.entry.bind("", self._hideEntry)
            self.entry.bind("", self._hideEntry)
    

    2) temporary entry

    When a key is hit, show the entry with place. entry.place(relx=1, anchor=tk.NE) will show the entry above the tree in the top right corner. If the key pressed is a letter, copy this letter in the entry. Set the focus on the entry so that following key press land in it.

    Symmetrically, when Escape or Return is hit while on the entry, flush the content, hide the entry (place_forget), and set the focus to the tree (otherwise, the entry keep the focus, even if not visible).

        def _keyOnTree(self, event):
            self.entry.place(relx=1, anchor=tk.NE)
            if event.char.isalpha():
                self.entry.insert(tk.END, event.char)
            self.entry.focus_set()
    
        def _hideEntry(self, event):
            self.entry.delete(0, tk.END)
            self.entry.place_forget()
            self.focus_set()
    

    3) actual search code

    You are free to search your items the way you want (depth or breadth first, match start or or full string...). Here is an example reusing A Rodas's answer and ignoring case.

        def _search(self, *args):
            pattern = self._toSearch.get()
            #avoid search on empty string
            if len(pattern) > 0:
                self.search(pattern)
    
        def search(self, pattern, item=''):
            children = self.get_children(item)
            for child in children:
                text = self.item(child, 'text')
                if text.lower().startswith(pattern.lower()):
                    self.selection_set(child)
                    self.see(child)
                    return True
                else:
                    res = self.search(pattern, child)
                    if res:
                        return True
    

提交回复
热议问题