从 tkinter 中的另一个 class 更新数据框
Update dataframe from another class in tkinter
我想更新 tkinter 中显示的数据框,从另一个 class 获取数据。
在我的应用程序中,我使用 classes 定义了框架。通过更改 class 中的输入参数,数据框应在另一个 class.
中更新
例如,我select class Commands
中OptionMenu
中的乘数和class中显示的数据框中的B列] Table
应按此乘数更新。但是当我更改乘数时,数据框不会更新。我在这里使用 Treeview
来显示数据框。
当我启动应用程序时,GUI 如下所示。右侧的窄条纹是空数据框。我将它初始化为空,即使我更改了乘数,它仍然是空的。相反,它应该看起来像之前的屏幕截图。
当然,完整的应用程序要复杂得多,但在这里我为了问题的缘故简化了一些事情。实际上,数据框包含很多列,计算相当复杂。
我尝试通过控制器方法传递对象。例如,在 class Table(显示数据框)中,我定义:
class Table(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.parent = parent
self.controller = controller # save the reference to the controller in each class
...
get_metrics = self.controller.get_page(Commands)
self.metrics = get_metrics.metrics
我(错误地)假设要在此帧中显示的数据帧 self.metrics
通过控制器对象调用函数 get_page()
得到更新。函数 get_page
从 class Commands
中获取对象 metrics
并在主 class sampleApp
中定义为:
def get_page(self, page_class):
return self.frames[page_class]
其他 class 命令包含相同的方法父/控制器以允许对象在 class 之间传递。
class Commands(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.parent = parent
self.controller = controller # save the reference to the controller in each class
self.metrics = pd.DataFrame()
在 class Commands
中,我将数据框 self.metrics
初始化为空(但这并不重要)。稍后在 class Commands
中,我使用函数 calculate_df
更新数据框,该函数不 return 任何东西,应该保持不变。因此,那里计算的数据框对象定义为global
.
我使用调用以下两行的 OptionMenu
方法更新 class Commands
中的数据框 self.metrics
:
calculate_df(mult = self.mult)
self.metrics = df
这里的df
是global
,我故意不直接用命令定义self.metrics = calculate_df(...)
。
函数 calculate_df
创建一个包含两列的数据框,其中 B 列相乘。例如乘数 2 df
变为:
A B
0 0 0
1 1 2
2 2 4
3 3 6
4 4 8
我post下面是完整的代码。
import numpy as np
import pandas as pd
pd.set_option('display.max_columns', 20)
pd.set_option('display.width', 200)
import tkinter as tk
from tkinter import ttk
def calculate_df(mult = 1.0):
global df
columns = [np.arange(5), np.arange(5) * mult]
df = pd.DataFrame(data=np.array(columns).T, columns=['A', 'B'])
class sampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.wm_title(self, "sampleApp")
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
frame = Commands(parent=container, controller=self)
self.frames[Commands] = frame
frame.grid(row=0, column=0)
# frame.pack()
self.show_frame(Commands)
frame = Table(parent=container, controller=self)
self.frames[Table] = frame
frame.grid(row=0, column=1)
# frame.pack()
self.show_frame(Table)
def show_frame(self, frame_name):
frame = self.frames[frame_name]
frame.tkraise()
def get_page(self, page_class):
return self.frames[page_class]
class Table(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.parent = parent
self.controller = controller # save the reference to the controller in each class
table_frame = tk.Frame(self, bd=1, relief=tk.RIDGE)
table_frame.pack(fill='x')
get_metrics = self.controller.get_page(Commands)
self.metrics = get_metrics.metrics
# columns = [np.arange(5), np.arange(5) * 2]
# df = pd.DataFrame(data=np.array(columns).T, columns=['A', 'B'])
# self.metrics = df
tv1 = ttk.Treeview(table_frame)
tv1.pack()
def display_metrics():
tv1.delete(*tv1.get_children())
tv1["column"] = list(self.metrics.columns)
tv1["show"] = "headings"
for column in tv1["columns"]:
tv1.heading(column, text=column) # set column heading
df_rows = self.metrics.to_numpy().tolist() # convert dataframe to list
for row in df_rows:
# inserts each list into the treeview.
tv1.insert("", "end", values=row)
display_metrics()
class Commands(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.parent = parent
self.controller = controller # save the reference to the controller in each class
self.metrics = pd.DataFrame()
side_frame = tk.Frame(self, relief=tk.RIDGE)
side_frame.pack()
buttons_frame = tk.Frame(side_frame, bd=1, relief=tk.RIDGE)
buttons_frame.pack(fill='x')
Lab1 = tk.Label(buttons_frame, text="multiplier", anchor=tk.W)
Lab1.grid(row=1, column=0)
def set_multiplier(*args):
self.mult = float(mult_var.get())
print("multiplier", self.mult)
calculate_df(mult = self.mult)
self.metrics = df
mult_var = tk.StringVar(self)
mult_var.set('')
mult_var.trace("w", set_multiplier)
opt_mult = tk.OptionMenu(buttons_frame, mult_var, *[1, 2, 3, 4])
opt_mult.grid(row=2, column=2, columnspan=1)
if __name__ == '__main__':
app = sampleApp()
app.geometry("+35+35")
app.mainloop()
您可以创建一个 static method
来实现此目的。
您将需要阅读一些有关该主题的内容。
但总之。您在 Table class 中定义了一个静态方法,然后您可以在命令 class 中调用该函数而无需初始化 Table class 的实例。可以使用 Table.my_method(value)
.
调用静态方法
首先将tv1
更改为实例变量self.tv1
,将嵌套函数display_metrics()
更改为Table
的class函数。还将所需的 metrics 作为参数传递给 display_metrics()
。
然后你可以在Commands
里面的嵌套函数set_multiplier()
里面直接调用Table.display_metrics()
class:
...
class Table(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.parent = parent
self.controller = controller # save the reference to the controller in each class
table_frame = tk.Frame(self, bd=1, relief=tk.RIDGE)
table_frame.pack(fill='x')
# changed tv1 to instance variable self.tv1
self.tv1 = ttk.Treeview(table_frame, show="headings")
self.tv1.pack()
# change display_metrics() to class function with added argument metrics
def display_metrics(self, metrics):
self.tv1.delete(*self.tv1.get_children())
self.tv1["column"] = list(metrics.columns)
for column in self.tv1["columns"]:
self.tv1.heading(column, text=column) # set column heading
df_rows = metrics.to_numpy().tolist() # convert dataframe to list
for row in df_rows:
# inserts each list into the treeview.
self.tv1.insert("", "end", values=row)
class Commands(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.parent = parent
self.controller = controller # save the reference to the controller in each class
self.metrics = pd.DataFrame()
side_frame = tk.Frame(self, relief=tk.RIDGE)
side_frame.pack()
buttons_frame = tk.Frame(side_frame, bd=1, relief=tk.RIDGE)
buttons_frame.pack(fill='x')
Lab1 = tk.Label(buttons_frame, text="multiplier", anchor=tk.W)
Lab1.grid(row=1, column=0)
def set_multiplier(*args):
self.mult = float(mult_var.get())
print("multiplier", self.mult)
calculate_df(mult = self.mult)
self.metrics = df
# update table
self.controller.get_page(Table).display_metrics(self.metrics)
mult_var = tk.StringVar(self)
mult_var.set('')
mult_var.trace("w", set_multiplier)
opt_mult = tk.OptionMenu(buttons_frame, mult_var, *[1, 2, 3, 4])
opt_mult.grid(row=2, column=2, columnspan=1)
...
请注意,最好将嵌套函数 set_multiplier()
也更改为 class 方法。
我想更新 tkinter 中显示的数据框,从另一个 class 获取数据。
在我的应用程序中,我使用 classes 定义了框架。通过更改 class 中的输入参数,数据框应在另一个 class.
中更新例如,我select class Commands
中OptionMenu
中的乘数和class中显示的数据框中的B列] Table
应按此乘数更新。但是当我更改乘数时,数据框不会更新。我在这里使用 Treeview
来显示数据框。
当我启动应用程序时,GUI 如下所示。右侧的窄条纹是空数据框。我将它初始化为空,即使我更改了乘数,它仍然是空的。相反,它应该看起来像之前的屏幕截图。
当然,完整的应用程序要复杂得多,但在这里我为了问题的缘故简化了一些事情。实际上,数据框包含很多列,计算相当复杂。
我尝试通过控制器方法传递对象。例如,在 class Table(显示数据框)中,我定义:
class Table(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.parent = parent
self.controller = controller # save the reference to the controller in each class
...
get_metrics = self.controller.get_page(Commands)
self.metrics = get_metrics.metrics
我(错误地)假设要在此帧中显示的数据帧 self.metrics
通过控制器对象调用函数 get_page()
得到更新。函数 get_page
从 class Commands
中获取对象 metrics
并在主 class sampleApp
中定义为:
def get_page(self, page_class):
return self.frames[page_class]
其他 class 命令包含相同的方法父/控制器以允许对象在 class 之间传递。
class Commands(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.parent = parent
self.controller = controller # save the reference to the controller in each class
self.metrics = pd.DataFrame()
在 class Commands
中,我将数据框 self.metrics
初始化为空(但这并不重要)。稍后在 class Commands
中,我使用函数 calculate_df
更新数据框,该函数不 return 任何东西,应该保持不变。因此,那里计算的数据框对象定义为global
.
我使用调用以下两行的 OptionMenu
方法更新 class Commands
中的数据框 self.metrics
:
calculate_df(mult = self.mult)
self.metrics = df
这里的df
是global
,我故意不直接用命令定义self.metrics = calculate_df(...)
。
函数 calculate_df
创建一个包含两列的数据框,其中 B 列相乘。例如乘数 2 df
变为:
A B
0 0 0
1 1 2
2 2 4
3 3 6
4 4 8
我post下面是完整的代码。
import numpy as np
import pandas as pd
pd.set_option('display.max_columns', 20)
pd.set_option('display.width', 200)
import tkinter as tk
from tkinter import ttk
def calculate_df(mult = 1.0):
global df
columns = [np.arange(5), np.arange(5) * mult]
df = pd.DataFrame(data=np.array(columns).T, columns=['A', 'B'])
class sampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.wm_title(self, "sampleApp")
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
frame = Commands(parent=container, controller=self)
self.frames[Commands] = frame
frame.grid(row=0, column=0)
# frame.pack()
self.show_frame(Commands)
frame = Table(parent=container, controller=self)
self.frames[Table] = frame
frame.grid(row=0, column=1)
# frame.pack()
self.show_frame(Table)
def show_frame(self, frame_name):
frame = self.frames[frame_name]
frame.tkraise()
def get_page(self, page_class):
return self.frames[page_class]
class Table(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.parent = parent
self.controller = controller # save the reference to the controller in each class
table_frame = tk.Frame(self, bd=1, relief=tk.RIDGE)
table_frame.pack(fill='x')
get_metrics = self.controller.get_page(Commands)
self.metrics = get_metrics.metrics
# columns = [np.arange(5), np.arange(5) * 2]
# df = pd.DataFrame(data=np.array(columns).T, columns=['A', 'B'])
# self.metrics = df
tv1 = ttk.Treeview(table_frame)
tv1.pack()
def display_metrics():
tv1.delete(*tv1.get_children())
tv1["column"] = list(self.metrics.columns)
tv1["show"] = "headings"
for column in tv1["columns"]:
tv1.heading(column, text=column) # set column heading
df_rows = self.metrics.to_numpy().tolist() # convert dataframe to list
for row in df_rows:
# inserts each list into the treeview.
tv1.insert("", "end", values=row)
display_metrics()
class Commands(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.parent = parent
self.controller = controller # save the reference to the controller in each class
self.metrics = pd.DataFrame()
side_frame = tk.Frame(self, relief=tk.RIDGE)
side_frame.pack()
buttons_frame = tk.Frame(side_frame, bd=1, relief=tk.RIDGE)
buttons_frame.pack(fill='x')
Lab1 = tk.Label(buttons_frame, text="multiplier", anchor=tk.W)
Lab1.grid(row=1, column=0)
def set_multiplier(*args):
self.mult = float(mult_var.get())
print("multiplier", self.mult)
calculate_df(mult = self.mult)
self.metrics = df
mult_var = tk.StringVar(self)
mult_var.set('')
mult_var.trace("w", set_multiplier)
opt_mult = tk.OptionMenu(buttons_frame, mult_var, *[1, 2, 3, 4])
opt_mult.grid(row=2, column=2, columnspan=1)
if __name__ == '__main__':
app = sampleApp()
app.geometry("+35+35")
app.mainloop()
您可以创建一个 static method
来实现此目的。
您将需要阅读一些有关该主题的内容。
但总之。您在 Table class 中定义了一个静态方法,然后您可以在命令 class 中调用该函数而无需初始化 Table class 的实例。可以使用 Table.my_method(value)
.
首先将tv1
更改为实例变量self.tv1
,将嵌套函数display_metrics()
更改为Table
的class函数。还将所需的 metrics 作为参数传递给 display_metrics()
。
然后你可以在Commands
里面的嵌套函数set_multiplier()
里面直接调用Table.display_metrics()
class:
...
class Table(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.parent = parent
self.controller = controller # save the reference to the controller in each class
table_frame = tk.Frame(self, bd=1, relief=tk.RIDGE)
table_frame.pack(fill='x')
# changed tv1 to instance variable self.tv1
self.tv1 = ttk.Treeview(table_frame, show="headings")
self.tv1.pack()
# change display_metrics() to class function with added argument metrics
def display_metrics(self, metrics):
self.tv1.delete(*self.tv1.get_children())
self.tv1["column"] = list(metrics.columns)
for column in self.tv1["columns"]:
self.tv1.heading(column, text=column) # set column heading
df_rows = metrics.to_numpy().tolist() # convert dataframe to list
for row in df_rows:
# inserts each list into the treeview.
self.tv1.insert("", "end", values=row)
class Commands(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.parent = parent
self.controller = controller # save the reference to the controller in each class
self.metrics = pd.DataFrame()
side_frame = tk.Frame(self, relief=tk.RIDGE)
side_frame.pack()
buttons_frame = tk.Frame(side_frame, bd=1, relief=tk.RIDGE)
buttons_frame.pack(fill='x')
Lab1 = tk.Label(buttons_frame, text="multiplier", anchor=tk.W)
Lab1.grid(row=1, column=0)
def set_multiplier(*args):
self.mult = float(mult_var.get())
print("multiplier", self.mult)
calculate_df(mult = self.mult)
self.metrics = df
# update table
self.controller.get_page(Table).display_metrics(self.metrics)
mult_var = tk.StringVar(self)
mult_var.set('')
mult_var.trace("w", set_multiplier)
opt_mult = tk.OptionMenu(buttons_frame, mult_var, *[1, 2, 3, 4])
opt_mult.grid(row=2, column=2, columnspan=1)
...
请注意,最好将嵌套函数 set_multiplier()
也更改为 class 方法。