如何创建允许用户根据列中的值 (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()
我创建了一个 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()