searching in treeview and highlight/select the row that contains the item that is searched

Snezhina Trifonova picture Snezhina Trifonova · Sep 12, 2017 · Viewed 7.1k times · Source

I am making a simple GUI for a patient's list with patient's name and date of visiting, using tkinter and treeview, I have an entry where user should type the name of the patient and the idea is if the name of the patient is located in the list, the row (or rows) which contain patient's name to be highlighted(selected). Or the other option can be in the listbox with all patients, to display only the entries with the patient's name we search for.

I have not used treeview before and could not find much data about its functions and examples, so I am struggling with the selection/highlight part, any ideas would be helpful at this point....

My code so far is:

import tkinter
from tkinter import ttk

class MainPage:

    def __init__(self,master):

        self.master = master
        self.frame = tkinter.Frame(self.master)
        self.master.columnconfigure(0, weight=1)
        self.master.columnconfigure(1, weight=3)
        self.master.columnconfigure(2, weight=1)
        self.master.columnconfigure(3, weight=1)
        self.master.columnconfigure(4, weight=1)

        self.searchfield = tkinter.Frame(self.master)
        self.searchfield.grid(row=1, column=0, columnspan=4)

        self.search_var = tkinter.StringVar()
        self.search_var.trace("w", lambda name, index, mode: self.selected)
        self.entry = tkinter.Entry(self.searchfield, 
                     textvariable=self.search_var, width=45)
        self.entry.grid(row=0, column=0, padx=10, pady=3)
        self.searchbtn = tkinter.Button(self.searchfield, text='Search', 
                         command=self.selected)
        self.searchbtn.grid(row=0, column=1)
        self.treeFrame = tkinter.Listbox(self.searchfield, width=45, height=45)
        self.treeFrame.grid(row=1, column=0, padx=10, pady=3)


        self.tree = ttk.Treeview( self.treeFrame, columns=('Name', 'Date'))
        self.tree.heading('#0', text='ID')
        self.tree.heading('#1', text='Name')
        self.tree.heading('#2', text='Date')
        self.tree.column('#1', stretch=tkinter.YES)
        self.tree.column('#2', stretch=tkinter.YES)
        self.tree.column('#0', stretch=tkinter.YES)
        self.tree.grid(row=4, columnspan=4, sticky='nsew')
        self.treeview = self.tree

        self.i = 1
        self.patient_list = [{"Name": "Jane", "Date": "05.09.2017"},
                             {"Name": "David", "Date": "04.09.2017"},
                             {"Name": "Patrick", "Date": "03.09.2017"}]
        for p in self.patient_list:
            self.tree.insert('', 'end', text="ID_"+str(self.i), values=
                             (p["Name"], p["Date"]))
            self.i = self.i + 1

        self.search_item = self.entry.get()
        for p in self.patient_list:
            if p["Name"] == self.search_item:
                self.selected(self.search_item)


    def selected(self):
        currentItem = self.tree.focus()
        print(self.tree.item(currentItem)['values'])


 root=tkinter.Tk()
 d=MainPage(root)
 root.mainloop()

Thanks in advance!

Answer

Ethan Field picture Ethan Field · Sep 14, 2017

Please see my explained snippet below:

from tkinter import *
from tkinter import ttk

class App:
    def __init__(self, root):
        self.root = root
        self.tree = ttk.Treeview(self.root) #create tree
        self.sv = StringVar() #create stringvar for entry widget
        self.sv.trace("w", self.command) #callback if stringvar is updated
        self.entry = Entry(self.root, textvariable=self.sv) #create entry
        self.names = ["Jane", "Janet", "James", "Jamie"] #these are just test inputs for the tree
        self.ids = [] #creates a list to store the ids of each entry in the tree
        for i in range(len(self.names)):
            #creates an entry in the tree for each element of the list
            #then stores the id of the tree in the self.ids list
            self.ids.append(self.tree.insert("", "end", text=self.names[i]))
        self.tree.pack()
        self.entry.pack()
    def command(self, *args):
        self.selections = [] #list of ids of matching tree entries
        for i in range(len(self.names)):
            #the below if check checks if the value of the entry matches the first characters of each element
            #in the names list up to the length of the value of the entry widget
            if self.entry.get() != "" and self.entry.get() == self.names[i][:len(self.entry.get())]:
                self.selections.append(self.ids[i]) #if it matches it appends the id to the selections list
        self.tree.selection_set(self.selections) #we then select every id in the list

root = Tk()
App(root)
root.mainloop()

So with this, every time the entry widget is updated, we cycle through the list of names and check if the value of the entry widget matches the value of the element in the names list up to the length of the value of the entry widget (EG, if we enter a five character long string then we check against the first five characters of the element).

If they match then we append the id of the tree entry to a list.

After all the names have been checked we pass the list of matching ids into self.tree.selection_set() which then highlights all of the matching tree entries.