如何创建允许用户根据列中的值 (python) 过滤树视图的按钮或选项?

How to create a button or option which allows a user to filter a treeview based on a value in a column (python)?

我创建了一个 GUI,其中基于下拉菜单中的货币 selection 显示体积数据。显示的数据框如下例所示。使用 Treeview 显示数据框。 我想为用户创建选项来过滤它。为了加载主框架,下拉列表包含本例中的主要货币欧元,因此当它被 selected 时,显示所有货币对兑欧元的交易量。一旦树视图打印在屏幕上并将视图过滤到它,我想为用户提供 select 特定货币对的选项。我读到过关于专注于一棵树的文章,但它似乎只适用于一行而不是整个块。实现我的目标最好的方法是什么?

from tkinter import *
import pandas as pd


class Application(Frame):
    def __init__(self, master=None):
        Frame.__init__(self, master)
        self.grid()
        self.master.title("Volume")

        for c in range(2, 7):
            self.master.rowconfigure(c, weight=1)

        for c in range(8):
            self.master.columnconfigure(c, weight=1)

        self.Frame1 = Frame(master, bg="blue")
        self.Frame1.grid(row=0, column=0, rowspan=1, columnspan=8, sticky=W + E + N + S)

        self.Frame2 = Frame(master, bg="lightblue")
        self.Frame2.grid(row=1, column=0, rowspan=1, columnspan=8, sticky=W + E + N + S)

        self.Frame3 = Frame(master, bg="white")
        self.Frame3.grid(row=2, column=0, rowspan=5, columnspan=8, sticky=W + E + N + S)

        self.Frame4 = Frame(master, bg="blue")
        self.Frame4.grid(row=7, column=0, rowspan=1, columnspan=8, sticky=W + E + N + S)

        label_title = Label(
            self.Frame1, text="Volume display", font=("Times New Roman", 30)
        )
        label_title.grid(row=0, column=1, padx=750)

        # drop down for currency
        label_option = Label(
            self.Frame2, text="Currency", font=("Times New Roman", 17, "bold")
        )
        label_option.grid(row=0, column=4)
        currencies = sorted(["USD", "GBP", "CAD", "EUR"])
        self.currency = StringVar(root)
        self.currency.set("EUR")
        option = OptionMenu(self.Frame2, self.currency, *currencies)
        option.grid(row=0, column=5)
        option["menu"].config(bg="white")
        option.config(font=("Times New Roman", 17), bg="white")

        # print df for currency
        self.Load_Df = Button(
            self.Frame4, text="Display Volume", command=self.load_data
        )
        self.Load_Df.config(font=("Times New Roman", 17), bg="white")
        self.Load_Df.grid(row=0, column=2, columnspan=2, ipadx=15)

        self.tree = ttk.Treeview(self.Frame3)

    def load_data(self):
        currency = self.currency.get()
        file_name = "D:/" + currency + "/volume.xlsx"
        final_df = pd.read_excel(file_name)

        self.clear_table()
        columns = list(final_df.columns)
        self.tree["columns"] = columns
        self.tree.pack(expand=TRUE, fill=BOTH)

        for i in columns:
            self.tree.column(i, anchor="w")
            self.tree.heading(i, text=i, anchor="w")

        for index, row in final_df.iterrows():
            self.tree.insert("", "end", text=index, values=list(row))

    def clear_table(self):
        for i in self.tree.get_children():
            self.tree.delete(i)


root = Tk()
app = Application(master=root)
app.mainloop()

您可以为用户创建一个选择框,只显示过滤后的结果。以下是根据您的代码使用 ttk.Combobox 的示例:

from tkinter import *
import pandas as pd
from tkinter import ttk

df = pd.DataFrame({"Currency":["EUR","GBR","CAD","EUR"],
                   "Volumne":[100,200,300,400]})

class Application(Tk):
    def __init__(self):
        Tk.__init__(self)
        self.title("Volume")

        self.tree = ttk.Treeview(self)
        columns = list(df.columns)
        self.combo = ttk.Combobox(self, values=list(df["Currency"].unique()),state="readonly")
        self.combo.pack()
        self.combo.bind("<<ComboboxSelected>>", self.select_currency)
        self.tree["columns"] = columns
        self.tree.pack(expand=TRUE, fill=BOTH)

        for i in columns:
            self.tree.column(i, anchor="w")
            self.tree.heading(i, text=i, anchor="w")

        for index, row in df.iterrows():
            self.tree.insert("", "end", text=index, values=list(row))

    def select_currency(self,event=None):
        self.tree.delete(*self.tree.get_children())
        for index, row in df.loc[df["Currency"].eq(self.combo.get())].iterrows():
            self.tree.insert("", "end", text=index, values=list(row))

root = Application()
root.mainloop()

我改进了@Henry Yik 的回答 - 这个版本将为所有可用列创建一个过滤器,您将能够同时过滤多个列。然而,它确实依赖于所有列都包含字符串来进行正确匹配的事实

from tkinter import *
from tkinter import ttk

inp = [{'Currency': 'EUR', 'Volume': '100', 'Country': 'SE'},
       {'Currency': 'GBR', 'Volume': '200', 'Country': 'SE'},
       {'Currency': 'CAD', 'Volume': '300', 'Country': 'SE'},
       {'Currency': 'EUR', 'Volume': '400', 'Country': 'SE'},
       {'Currency': 'EUR', 'Volume': '100', 'Country': 'DK'},
       {'Currency': 'GBR', 'Volume': '200', 'Country': 'DK'},
       {'Currency': 'CAD', 'Volume': '300', 'Country': 'DK'},
       {'Currency': 'EUR', 'Volume': '400', 'Country': 'DK'},
       ]


class Application(Tk):
    def __init__(self):
        Tk.__init__(self)
        self.title("Volume")

        combofr = Frame(self)
        combofr.pack(expand=True, fill=X)
        self.tree = ttk.Treeview(self, show='headings')
        columns = list(inp[0].keys())

        self.filters = []

        for col in columns:
            name = 'combo_' + col
            self.filters.append(name)
            setattr(self, name, ttk.Combobox(combofr, values=[''] + sorted(set(x[col] for x in inp)), state="readonly"))
            getattr(self, name).pack(side=LEFT, expand=True, fill=X)
            getattr(self, name).bind('<<ComboboxSelected>>', self.select_from_filters)

        self.tree["columns"] = columns
        self.tree.pack(expand=TRUE, fill=BOTH)

        for i in columns:
            self.tree.column(i, anchor="w")
            self.tree.heading(i, text=i, anchor="w")

        for i, row in enumerate(inp):
            self.tree.insert("", "end", text=i, values=list(row.values()))

    def select_from_filters(self, event=None):
        self.tree.delete(*self.tree.get_children())

        all_filter = lambda x: all(x[f.split('_')[-1]] == getattr(self, f).get() or getattr(self, f).get() == '' for f in self.filters)
        for row in inp:
            if all_filter(row):
                self.tree.insert("", "end", values=list(row.values()))


root = Application()
root.mainloop()