Python Tkinter中访问非严格父类的变量与方法
问题解答
1. 关于Table与Application的关系理解
你的理解完全正确:Table和Application并非严格意义上的父子类(继承关系),而是组合关系——Application类实例化了Table对象并使用它。具体来说:
Table继承自tk.Frame,是一个自定义UI组件;Application继承自tk.Tk,是整个应用的主窗口;Application内部创建Table实例时将自身作为master传递,二者是使用者与被使用者的关系,不存在继承关联。
2. 实现行增删时更新total并调用show_total
方案1:直接通过master引用操作(简单直接)
利用Table的master属性就是Application实例的特性,直接在Table的增删方法中操作Application的变量和方法:
修改后的Table类代码:
class Table(tk.Frame): def __init__(self, master, header_labels:tuple, *args, **kwargs): tk.Frame.__init__(self, master, *args, **kwargs) self.lbl_cfg = { 'master' : self, 'foreground' : 'blue', 'relief' : 'raised', 'font' : 'Arial 16 bold', 'padx' : 0, 'pady' : 0, 'borderwidth': 1, 'width' : 11, } self.headers = [] self.rows = [] for col, lbl in enumerate(header_labels): self.grid_columnconfigure(col, weight=1) (header := tk.Label(text=lbl, **self.lbl_cfg)).grid(row=0, column=col, sticky='nswe') self.headers.append(header) def add_row(self, desc:str, quantity:int, rate:float, amt:float, pending:bool) -> None: # 更新total并调用show_total self.master.total += amt self.master.show_total() self.rows.append([]) for col, lbl in enumerate((desc, quantity, rate, amt, pending), 1): (entry := tk.Label(text=lbl, **self.lbl_cfg)).grid(row=len(self.rows), column=col, sticky='nswe') self.rows[-1].append(entry) def del_row(self, i:int) -> None: # 获取要删除行的金额并更新total amt = float(self.rows[i][3]['text']) self.master.total -= amt self.master.show_total() for ent in self.rows[i]: ent.destroy() del self.rows[i] for r, row in enumerate(self.rows, 1): for c, ent in enumerate(row, 1): ent.grid_forget() ent.grid(row=r, column=c, sticky='nswe')
方案2:使用回调函数(低耦合,更灵活)
如果希望降低类之间的耦合度,可以在初始化Table时传入回调函数,由Table触发回调更新总额:
修改后的Application类:
class Application(tk.Tk): def __init__(self, title:str="Sample Application", x:int=0, y:int=0, **kwargs): tk.Tk.__init__(self) self.title(title) self.config(**kwargs) header_labels = ('', 'Description', 'Quantity', 'Rate', 'Amt', 'pending') self.total=0 # 传入更新总额的回调函数 self.table = Table(self, header_labels, update_callback=self.update_total) self.table.grid(row=0, column=0, sticky='nswe') self.update_idletasks() self.geometry(f'{self.winfo_width()}x{self["height"] or self.winfo_screenheight()}+{x}+{y}') # test self.table.add_row("A", 2, 12.5, 25, True) self.table.add_row("B", 4, 12.5, 50, False) self.table.del_row(0) self.table.add_row("A", 2, 12.5, 25, True) def update_total(self, amt, is_add=True): if is_add: self.total += amt else: self.total -= amt self.show_total() def show_total(self): print(self.total) print("Total has been updated") return
修改后的Table类:
class Table(tk.Frame): def __init__(self, master, header_labels:tuple, update_callback=None, *args, **kwargs): tk.Frame.__init__(self, master, *args, **kwargs) self.update_callback = update_callback self.lbl_cfg = { 'master' : self, 'foreground' : 'blue', 'relief' : 'raised', 'font' : 'Arial 16 bold', 'padx' : 0, 'pady' : 0, 'borderwidth': 1, 'width' : 11, } self.headers = [] self.rows = [] for col, lbl in enumerate(header_labels): self.grid_columnconfigure(col, weight=1) (header := tk.Label(text=lbl, **self.lbl_cfg)).grid(row=0, column=col, sticky='nswe') self.headers.append(header) def add_row(self, desc:str, quantity:int, rate:float, amt:float, pending:bool) -> None: # 触发回调更新总额 if self.update_callback: self.update_callback(amt, is_add=True) self.rows.append([]) for col, lbl in enumerate((desc, quantity, rate, amt, pending), 1): (entry := tk.Label(text=lbl, **self.lbl_cfg)).grid(row=len(self.rows), column=col, sticky='nswe') self.rows[-1].append(entry) def del_row(self, i:int) -> None: # 获取要删除行的金额并触发回调 amt = float(self.rows[i][3]['text']) if self.update_callback: self.update_callback(amt, is_add=False) for ent in self.rows[i]: ent.destroy() del self.rows[i] for r, row in enumerate(self.rows, 1): for c, ent in enumerate(row, 1): ent.grid_forget() ent.grid(row=r, column=c, sticky='nswe')
两种方案均可实现需求:方案1简单直接,适合小型项目;方案2耦合度低,更利于代码维护和扩展。
内容的提问来源于stack exchange,提问作者user1371666
相关产品推荐
相关产品推荐

