searching in treeview and highlight/select the row

2019-06-13 20:26发布

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!

2条回答
The star\"
2楼-- · 2019-06-13 20:43

Some sort of search already implemented right from the box, so there's no need in another one!

All you need to do is supply proper tags to your patients. After that you can search by these tags (yeah, you can supply multiple of them to a specified patient) and control appearance/highlighting of patients (rows of treeview).

Let's play around:

class MainPage:
    def __init__(self,master):
        #   ...
        for p in self.patient_list:
            #   Note tags argument, right now we use names of patients
            self.tree.insert('', 'end', text="ID_" + str(self.i), values=
                             (p["Name"], p["Date"]), tags=p["Name"])
        #   ...

    #   ...
    def selected(self):
        #   setting selection by iids with tag (name of a patient or whatever)
        self.tree.selection_set(self.tree.tag_has(self.search_var.get()))
    #   ...

Right now this just highlight a patient... basic search ... but you can easily modify this with pair of .detach() and .move() to sort entire treeview.

Also, you can implement a partial search with few lines of code:

class MainPage:
    #   ...
    def selected(self):
        search_for = self.search_var.get()
        iid_to_select = ()

        #   if there's any sense in search
        if search_for != '':
            #   get all tags from tkinter
            all_tags = self.master.tk.call(str(self.tree), "tag", "names")

            #   sort tags by search query
            tags_to_select = tuple(filter(lambda tag: search_for.lower() in tag.lower(), all_tags))

            #   gather iids by tags to select
            for sorted_tag in tags_to_select:
                iid_to_select += self.tree.tag_has(sorted_tag)

        #   setting selection by iids
        self.tree.selection_set(iid_to_select)
    #   ...

partial search

In conclusion, there's no need in reinventing the wheel, but if your treeview is mutable by user - keep in mind that these tags must be synched with mutable content.

More about treeview can be found here.

查看更多
▲ chillily
3楼-- · 2019-06-13 20:49

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.

查看更多
登录 后发表回答