Reputation: 45
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!
Upvotes: 2
Views: 13548
Reputation: 4482
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...
... 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)
# ...
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.
Upvotes: 4
Reputation: 4730
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 id
s into self.tree.selection_set()
which then highlights all of the matching tree entries.
Upvotes: 4