Search code examples
pythonsearchtkintertreeviewtkinter-entry

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


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!


Solution

  • 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.