如何定义Tkinter控制器?弹窗跨类调用函数报错求助
Let's break down your problems step by step and fix them, along with clarifying how the get_page function works:
核心问题分析
1. NameError: name 'controller' is not defined
你在BoundingBox.popup()方法里调用popupWindow(self.master, controller)时,controller这个变量根本没定义。实际上你的BoundingBox类本身就是控制器,所以应该传递self(当前的BoundingBox实例)。另外,self.master在这里也是错误的——因为BoundingBox继承自tk.Tk,它自己就是主窗口,所以master参数应该传self。
2. 弹窗销毁代码错误
你的popupWindow.cleanup()方法里用了self.top.destroy(),但popupWindow继承自tk.Toplevel,不存在self.top这个属性,直接调用self.destroy()就能关闭弹窗。
3. get_page函数未完成
原代码里的get_page引用了self.frames,但你没有初始化这个字典。这个函数是Tkinter多页面应用的常用工具,用来管理不同页面的实例,需要先在BoundingBox的__init__里初始化self.frames = {},还可以完善逻辑让它自动创建页面实例(如果未创建的话)。
4. 补充:values方法变量缺失
你的values()方法用到了self.start_x、self.allcord等变量,这些需要在__init__里提前初始化,否则会触发新的报错。
修改后的完整代码
import tkinter as tk from tkinter import Label, Entry, Button class BoundingBox(tk.Tk): def __init__(self): super().__init__() # 用super()替代直接调用父类构造,更简洁规范 # 初始化values方法依赖的变量 self.start_x = self.start_y = self.end_x = self.end_y = 0 self.totwidth = self.totheight = 0 self.value1 = self.value2 = "" self.allcord = [] self.allrect = [] # 初始化frames字典,用于get_page管理页面实例 self.frames = {} def get_page(self, page_class): """获取或创建指定类的页面实例,用于多页面切换""" # 如果页面未创建,实例化并加入frames字典 if page_class not in self.frames: frame = page_class(self, self) # 传递主窗口和控制器 self.frames[page_class] = frame frame.grid(row=0, column=0, sticky="nsew") # 返回页面实例 return self.frames[page_class] def popup(self): # 正确传递主窗口和控制器(当前BoundingBox实例) self.w = popupWindow(self, self) def values(self): print(self.start_x, self.start_y, self.end_x, self.end_y, self.totwidth, self.totheight, self.value1, self.value2) self.allcord.append([self.start_x, self.start_y, self.end_x, self.end_y, self.totwidth, self.totheight, self.value1, self.value2]) # 注意:self.rect需要你在其他函数中定义,否则会报错 self.allrect.append(self.rect) print(len(self.allcord)) class popupWindow(tk.Toplevel): def __init__(self, master, controller): super().__init__(master) self.controller = controller self.title("Input Breed & Color") # 布局控件并添加间距 self.l1 = Label(self, text="Breed") self.l1.grid(row=0, column=0, padx=5, pady=5) self.e1 = Entry(self) self.e1.grid(row=0, column=1, padx=5, pady=5) self.l2 = Label(self, text="Color") self.l2.grid(row=1, column=0, padx=5, pady=5) self.e2 = Entry(self) self.e2.grid(row=1, column=1, padx=5, pady=5) self.b = Button(self, text='Save', command=self.cleanup) self.b.grid(row=2, column=1, padx=5, pady=5) def cleanup(self): # 将弹窗输入的值赋值给控制器的属性 self.controller.value1 = self.e1.get() self.controller.value2 = self.e2.get() # 调用控制器的values方法 self.controller.values() # 正确关闭弹窗 self.destroy() if __name__ == "__main__": draw = BoundingBox() # 添加测试按钮触发弹窗(你可以根据需求替换成自己的触发逻辑) test_btn = Button(draw, text="Open Input Popup", command=draw.popup) test_btn.pack(pady=20) draw.mainloop()
关于get_page函数的说明
这个函数是Tkinter多页面应用的标准工具,作用是:
- 避免重复创建相同页面的实例,节省资源
- 统一管理所有页面,方便在不同页面之间切换或调用方法
举个使用例子,如果你有一个设置页面:
class SettingsPage(tk.Frame): def __init__(self, parent, controller): super().__init__(parent) self.controller = controller Label(self, text="Settings Page").pack(pady=20) # 点击按钮切换回主页面 Button(self, text="Go Back to Main", command=lambda: controller.get_page(BoundingBox)).pack()
然后在主类中调用self.get_page(SettingsPage)就能获取这个页面的实例,直接切换到该页面。
内容的提问来源于stack exchange,提问作者AnalogDigital

