You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何调用处于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的组合来实现滚动效果,具体修改如下:

核心思路:

  1. 创建滚动条和Canvas容器
  2. 在Canvas内部放一个inner_frame,用来承载所有输入控件
  3. 绑定事件让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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 18:07:33