Home > Blockchain >  How can I add a search function to a treeview?
How can I add a search function to a treeview?

Time:11-29

I want to display a row that matches the entry from all of my entry boxes into the treeview. enter image description here How can I get the values of the treeview and check if it matches the entry from one of the boxes and display the whole row. Here is my treeview code

tree = ttk.Treeview()
                books_data = pandas.read_csv("List of Books - Sheet1 (3).csv")
                df_column = books_data.columns.values
                print(len(df_column))
                print(df_column)
                tree["column"] = list(books_data.columns)
                tree["show"] = "headings"

                for column in tree['column']:
                    tree.heading(column,text=column)

                df_rows = books_data.to_numpy().tolist()
                for row in df_rows:
                    tree.insert("","end",values=row)
                tree.grid(column=0,row=4,columnspan=8)

CodePudding user response:

The short solution

import tkinter as tk
import tkinter.ttk as ttk
import sys
import pandas

# https://gist.githubusercontent.com/netj/8836201/raw/6f9306ad21398ea43cba4f7d537619d0e07d5ae3/iris.csv
df = pandas.read_csv('./iris.csv')
df.head()

class main_window:
    def __init__(self, root):
        self.root = root
        root.title("Treeview Search Example")
        
        # INITIALIZE TREEVIEW   SCROLLVIEW
        self.tree = ttk.Treeview(root, columns=list(df.columns.values), show='headings')
        self.tree.grid(row=1, column=0, sticky='nsew')
        
        # https://stackoverflow.com/a/41880534/5210078
        vsb = ttk.Scrollbar(root, orient="vertical", command=self.tree.yview)
        vsb.grid(row=1, column=1, sticky='ns')
        self.tree.configure(yscrollcommand=vsb.set)
        
        for column in self.tree['column']:
            self.tree.heading(column,text=column)

        df_rows = df.to_numpy().tolist()
        for row in df_rows:
            self.tree.insert("","end",values=row)

        # ADD SEARCH BOXES
        search_frame = tk.Frame(root)
        search_frame.grid(row=0, column=0, columnspan=2, sticky='nsew')
        tk.Label(search_frame, text="sepal.length:").grid(row=0, column=0)
        tk.Label(search_frame, text="sepal.width:").grid(row=0, column=2)
        tk.Label(search_frame, text="petal.length:").grid(row=0, column=4)
        tk.Label(search_frame, text="petal.width:").grid(row=0, column=6)
        tk.Label(search_frame, text="variety:").grid(row=0, column=8)
        
        # Add Search boxes
        self.sepal_length_ent = tk.Entry(search_frame)
        self.sepal_length_ent.grid(row=0, column=1)
        self.sepal_width_ent = tk.Entry(search_frame)
        self.sepal_width_ent.grid(row=0, column=3)
        self.petal_length_ent = tk.Entry(search_frame)
        self.petal_length_ent.grid(row=0, column=5)
        self.petal_width_ent = tk.Entry(search_frame)
        self.petal_width_ent.grid(row=0, column=7)
        self.variety_ent = tk.Entry(search_frame)
        self.variety_ent.grid(row=0, column=9)
        
        tk.Button(search_frame, text="Search", command=self.search).grid(row=0, column=10)
        
    def search(self):
        # https://stackoverflow.com/a/27068344/5210078
        self.tree.delete(*self.tree.get_children())
        build_query = ""
        # https://stackoverflow.com/a/56157729/5210078
        if self.sepal_length_ent.get():
            build_query  = f'& {self.sepal_length_ent.get()} == `sepal.length` '
        if self.sepal_width_ent.get():
            build_query  = f'& {self.sepal_width_ent.get()} == `sepal.width` '
        if self.petal_length_ent.get():
            build_query  = f'& {self.petal_length_ent.get()} == `petal.length` '
        if self.petal_width_ent.get():
            build_query  = f'& {self.petal_width_ent.get()} == `petal.width` '
        if self.variety_ent.get():
            build_query  = f'& "{self.variety_ent.get()}" in `variety`'
        if build_query:
            print(build_query)
            queried_df = df.query(build_query[1:])
        else:
            queried_df = df
            
        df_rows = queried_df.to_numpy().tolist()
        for row in df_rows:
            self.tree.insert("","end",values=row)
            

if __name__ == '__main__':
    main = tk.Tk()
    main_window(main)
    main.mainloop()
    sys.exit()

The explanation of the solution

import tkinter as tk
import tkinter.ttk as ttk
import sys
import pandas

Obviously importing all the needed programs.

# https://gist.githubusercontent.com/netj/8836201/raw/6f9306ad21398ea43cba4f7d537619d0e07d5ae3/iris.csv
df = pandas.read_csv('./iris.csv')
df.head()

Loading the csv dataset. I used the IRIS dataset because it is easily accessible.

Jump ahead to:

if __name__ == '__main__':
    main = tk.Tk()
    main_window(main)
    main.mainloop()
    sys.exit()

Here we load the main_window class into our main tkinter window (makes your code look neater)

The self-explanatory bit (added a ttk treeview with scrollbar):

class main_window:
    def __init__(self, root):
        self.root = root
        root.title("Treeview Search Example")

        # INITIALIZE TREEVIEW   SCROLLVIEW
        self.tree = ttk.Treeview(root, columns=list(df.columns.values), show='headings')
        self.tree.grid(row=1, column=0, sticky='nsew')

        # https://stackoverflow.com/a/41880534/5210078
        vsb = ttk.Scrollbar(root, orient="vertical", command=self.tree.yview)
        vsb.grid(row=1, column=1, sticky='ns')
        self.tree.configure(yscrollcommand=vsb.set)

        for column in self.tree['column']:
            self.tree.heading(column,text=column)

        df_rows = df.to_numpy().tolist()
        for row in df_rows:
            self.tree.insert("","end",values=row)

        # ADD SEARCH BOXES
        search_frame = tk.Frame(root)
        search_frame.grid(row=0, column=0, columnspan=2, sticky='nsew')
        tk.Label(search_frame, text="sepal.length:").grid(row=0, column=0)
        tk.Label(search_frame, text="sepal.width:").grid(row=0, column=2)
        tk.Label(search_frame, text="petal.length:").grid(row=0, column=4)
        tk.Label(search_frame, text="petal.width:").grid(row=0, column=6)
        tk.Label(search_frame, text="variety:").grid(row=0, column=8)

        # Add Search boxes
        self.sepal_length_ent = tk.Entry(search_frame)
        self.sepal_length_ent.grid(row=0, column=1)
        self.sepal_width_ent = tk.Entry(search_frame)
        self.sepal_width_ent.grid(row=0, column=3)
        self.petal_length_ent = tk.Entry(search_frame)
        self.petal_length_ent.grid(row=0, column=5)
        self.petal_width_ent = tk.Entry(search_frame)
        self.petal_width_ent.grid(row=0, column=7)
        self.variety_ent = tk.Entry(search_frame)
        self.variety_ent.grid(row=0, column=9)

        tk.Button(search_frame, text="Search", command=self.search).grid(row=0, column=10)
    def search(self):
        # https://stackoverflow.com/a/27068344/5210078
        self.tree.delete(*self.tree.get_children())

Delete all the rows in the table

        build_query = ""
        # https://stackoverflow.com/a/56157729/5210078
        if self.sepal_length_ent.get():
            build_query  = f'& {self.sepal_length_ent.get()} == `sepal.length` '
        if self.sepal_width_ent.get():
            build_query  = f'& {self.sepal_width_ent.get()} == `sepal.width` '
        if self.petal_length_ent.get():
            build_query  = f'& {self.petal_length_ent.get()} == `petal.length` '
        if self.petal_width_ent.get():
            build_query  = f'& {self.petal_width_ent.get()} == `petal.width` '
        if self.variety_ent.get():
            build_query  = f'& "{self.variety_ent.get()}" in `variety`'

Build a search query. Use == for integers/floats and use in when searching inside strings. If you want to read more about queries, read this. If you wanted to do something wacky, like make one of your entries a "lower than" box for integers, you could even substitute in a < or >. It basically makes our life much easier here! Because we can do a query, based off a dynamically changing string!

        if build_query:
            print(build_query)
            queried_df = df.query(build_query[1:])
        else:
            queried_df = df

If there is no build_query (no inputs in entry boxes), just reload the table with the data from the df. (if build_query: is shorthand for if build_query != '':) If there is query data, do a query with the query terms.

        df_rows = queried_df.to_numpy().tolist()
        for row in df_rows:
            self.tree.insert("","end",values=row)

Reload the treeview with the data from the "new" queried df!

If you wanted a solution relate to your actual csv and interface, I'd recommend sharing an example of your csv and the gui design.

What the solution became

         def search():
                    # clear the tree of data
                    tree.delete(*tree.get_children())
                    entries = [
                    title_entry,
                    author_entry,
                    subject_category_entry,
                    publication_date_entry
                    ]
                    build_df = books_data
                    if entries[0].get():
                        build_df = build_df[build_df.TITLE.str.contains(entries[0].get())]
                    if entries[1].get():
                        build_df = build_df[build_df.AUTHOR.str.contains(entries[1].get())]
                    if entries[2].get():
                        build_df = build_df[build_df.SUBJECT_CATEGORY.str.contains(entries[2].get())]    
                    if entries[3].get():
                        build_df = build_df[build_df.PUBLICATION_DATE == (entries[3].get())]    
                        
                    df_rows = build_df.to_numpy().tolist()
                    for row in df_rows:
                        tree.insert("","end",values=row)
  • Related