如何调用处于Tkinter主循环中的类?程序启动失败与滚动条实现问题咨询
问题分析与解决方案
我来帮你解决这两个困扰你的问题:
一、程序无法启动:无限递归调用的坑
你在CreatePage的__init__里写了Test = Database_Project(),这直接触发了无限递归循环:
- 程序启动时,
NoSQL_Project = Database_Project()创建主应用实例 Database_Project的初始化逻辑会创建CreatePage实例CreatePage初始化时又创建新的Database_Project实例,循环往复最终导致程序卡死
修复方案:
你完全不需要重新实例化Database_Project!因为CreatePage初始化时已经接收了controller参数,这个controller就是主应用Database_Project的实例。
如果想获取当前的CreatePage实例,直接用self就可以;如果要调用主应用的方法(比如get_current_frame),用self.controller就行。
把CreatePage里的这段错误代码:
Test = Database_Project() #active_frame = Test.get_current_frame("CreatePage")
替换成:
# 直接获取当前CreatePage实例,就是self本身 active_frame = self # 或者通过controller获取,效果完全一致 active_frame = self.controller.get_current_frame("CreatePage")
二、给CreatePage添加滚动条功能
Tkinter的Frame本身不支持滚动,我们可以用Canvas+Scrollbar的组合来实现滚动效果,具体修改如下:
核心思路:
- 创建滚动条和Canvas容器
- 在Canvas内部放一个
inner_frame,用来承载所有输入控件 - 绑定事件让Canvas自动调整滚动区域,确保所有内容都能被滚动到
修改后的CreatePage代码:
class CreatePage(tk.Frame): def __init__(self, parent, controller): tk.Frame.__init__(self, parent) self.controller = controller self.inputlist = [] self.newinputlist = [] # 1. 设置滚动条和Canvas容器 scrollbar = tk.Scrollbar(self, orient="vertical") scrollbar.pack(side="right", fill="y") self.canvas = tk.Canvas(self, yscrollcommand=scrollbar.set) self.canvas.pack(side="left", fill="both", expand=True) scrollbar.config(command=self.canvas.yview) # 2. 创建内部Frame承载所有控件 self.inner_frame = tk.Frame(self.canvas) self.canvas.create_window((0, 0), window=self.inner_frame, anchor="nw") # 绑定事件,自动更新滚动区域 self.inner_frame.bind("<Configure>", self.on_frame_configure) # --- 原来的控件创建代码,把所有self改成self.inner_frame --- labels = [tk.Label(self.inner_frame, text="Enter unique field"), tk.Label(self.inner_frame, text="Enter corresponding the value/s")] self.inputlist.append(labels[:]) for toplabels in range(1): self.inputlist[toplabels][0].grid(row=toplabels, column=0, padx=10, pady=5) self.inputlist[toplabels][1].grid(row=toplabels, column=1, padx=10, pady=5) first_entries = [tk.Entry(self.inner_frame, borderwidth=5), tk.Text(self.inner_frame, borderwidth=5, height= 5, width=20)] self.newinputlist.append(first_entries[:]) self.inputlist.append(first_entries[:]) for x in range(0, len(self.newinputlist) + 1): self.newinputlist[0][x].grid(row=1, column=x, padx=10, pady=5) button_input_1 = [tk.Button(self.inner_frame, text="ADD FIELD/VALUE", command=self.add_insert), tk.Button(self.inner_frame, text="BACK", command=lambda: controller.current_frame("MainPage"))] self.inputlist.append(button_input_1[:]) button_input_2 = [tk.Button(self.inner_frame, text="IMPORT FILE"), tk.Button(self.inner_frame, text="SUBMIT DATA", command=self.submit_data)] self.inputlist.append(button_input_2[:]) for button in range(len(self.inputlist) - 2, len(self.inputlist)): self.inputlist[button][0].grid(row=button, column=0, padx=10, pady=5) self.inputlist[button][1].grid(row=button, column=1, padx=10, pady=5) # 新增方法:更新Canvas滚动区域 def on_frame_configure(self, event): self.canvas.configure(scrollregion=self.canvas.bbox("all")) # 修改add_insert方法,适配内部Frame def add_insert(self): add_input = [tk.Entry(self.inner_frame, borderwidth=5), tk.Text(self.inner_frame, borderwidth=5, height= 5, width=20)] self.inputlist.insert(-2, add_input) self.newinputlist.append(add_input) for widget in self.inner_frame.children.values(): widget.grid_forget() for index, widgets in enumerate(self.inputlist): widget_one = widgets[0] widget_two = widgets[1] widget_one.grid(row=index, column=0, padx=10, pady=5) widget_two.grid(row=index, column=1, padx=10) # 添加控件后更新滚动区域 self.on_frame_configure(None) # 修改submit_data方法,适配内部Frame def submit_data(self): for index, entries in enumerate(self.newinputlist): my_label = Label(self.inner_frame, text=str(entries[0].get()) + str(entries[1].get("1.0", END))) my_label.grid(row=len(self.inputlist) + index, column=0) # 提交后更新滚动区域 self.on_frame_configure(None)
三、完整修复后的代码
from tkinter import * import tkinter as tk class Database_Project(tk.Tk): def __init__(self): tk.Tk.__init__(self) stack_frame_container = tk.Frame(self) stack_frame_container.grid_columnconfigure(0, weight=1) stack_frame_container.grid_rowconfigure(0, weight=1) stack_frame_container.pack(side="top", fill="both", expand=True) self.frameslist = {} for frame in (MainPage, CreatePage): frame_occurrence = frame.__name__ active_frame = frame(parent=stack_frame_container, controller=self) self.frameslist[frame_occurrence] = active_frame active_frame.grid(row=0, column=0, sticky="snew") self.current_frame("MainPage") print(self.frameslist) def current_frame(self, frame_occurrence): active_frame = self.frameslist[frame_occurrence] active_frame.tkraise() def get_current_frame(self, frame_occurrence): active_frame = self.frameslist[frame_occurrence] return active_frame class MainPage(tk.Frame): def __init__(self, parent, controller): tk.Frame.__init__(self, parent) self.controller = controller label_create = tk.Label(self, text="Create and insert data").grid(row=0, column=0, padx=50, pady=(50,0)) create_button = tk.Button(self, text="CREATE", command=lambda: controller.current_frame("CreatePage")).grid(row=1, column=0) label_read = tk.Label(self, text="Query over data").grid(row=0, column=1, padx=50, pady=(50,0)) read_button = tk.Button(self, text="READ").grid(row=1, column=1) label_update = tk.Label(self, text="Modify existing data").grid(row=2, column=0, padx=50, pady=(50,0)) update_button = tk.Button(self, text="UPDATE").grid(row=3, column=0, pady=(0,50)) label_delete = tk.Label(self, text="Remove data").grid(row=2, column=1, padx=50, pady=(50,0)) delete_button = tk.Button(self, text="DELETE").grid(row=3, column=1, pady=(0,50)) class CreatePage(tk.Frame): def __init__(self, parent, controller): tk.Frame.__init__(self, parent) self.controller = controller self.inputlist = [] self.newinputlist = [] # 滚动条和Canvas设置 scrollbar = tk.Scrollbar(self, orient="vertical") scrollbar.pack(side="right", fill="y") self.canvas = tk.Canvas(self, yscrollcommand=scrollbar.set) self.canvas.pack(side="left", fill="both", expand=True) scrollbar.config(command=self.canvas.yview) self.inner_frame = tk.Frame(self.canvas) self.canvas.create_window((0, 0), window=self.inner_frame, anchor="nw") self.inner_frame.bind("<Configure>", self.on_frame_configure) # 控件创建 labels = [tk.Label(self.inner_frame, text="Enter unique field"), tk.Label(self.inner_frame, text="Enter corresponding the value/s")] self.inputlist.append(labels[:]) for toplabels in range(1): self.inputlist[toplabels][0].grid(row=toplabels, column=0, padx=10, pady=5) self.inputlist[toplabels][1].grid(row=toplabels, column=1, padx=10, pady=5) first_entries = [tk.Entry(self.inner_frame, borderwidth=5), tk.Text(self.inner_frame, borderwidth=5, height= 5, width=20)] self.newinputlist.append(first_entries[:]) self.inputlist.append(first_entries[:]) for x in range(0, len(self.newinputlist) + 1): self.newinputlist[0][x].grid(row=1, column=x, padx=10, pady=5) button_input_1 = [tk.Button(self.inner_frame, text="ADD FIELD/VALUE", command=self.add_insert), tk.Button(self.inner_frame, text="BACK", command=lambda: controller.current_frame("MainPage"))] self.inputlist.append(button_input_1[:]) button_input_2 = [tk.Button(self.inner_frame, text="IMPORT FILE"), tk.Button(self.inner_frame, text="SUBMIT DATA", command=self.submit_data)] self.inputlist.append(button_input_2[:]) for button in range(len(self.inputlist) - 2, len(self.inputlist)): self.inputlist[button][0].grid(row=button, column=0, padx=10, pady=5) self.inputlist[button][1].grid(row=button, column=1, padx=10, pady=5) def on_frame_configure(self, event): self.canvas.configure(scrollregion=self.canvas.bbox("all")) def add_insert(self): add_input = [tk.Entry(self.inner_frame, borderwidth=5), tk.Text(self.inner_frame, borderwidth=5, height= 5, width=20)] self.inputlist.insert(-2, add_input) self.newinputlist.append(add_input) for widget in self.inner_frame.children.values(): widget.grid_forget() for index, widgets in enumerate(self.inputlist): widget_one = widgets[0] widget_two = widgets[1] widget_one.grid(row=index, column=0, padx=10, pady=5) widget_two.grid(row=index, column=1, padx=10) self.on_frame_configure(None) def submit_data(self): for index, entries in enumerate(self.newinputlist): my_label = Label(self.inner_frame, text=str(entries[0].get()) + str(entries[1].get("1.0", END))) my_label.grid(row=len(self.inputlist) + index, column=0) self.on_frame_configure(None) if __name__ == "__main__": NoSQL_Project = Database_Project() NoSQL_Project.title("NoSQL Database Project") NoSQL_Project.maxsize(500, 500) NoSQL_Project.mainloop()
内容的提问来源于stack exchange,提问作者Sloxy
相关产品推荐
相关产品推荐

