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

Tkinter多帧应用跨Frame变量识别问题求助:HomePage的second_frame无法被AddPage访问

解决Tkinter多帧间共享second_frame的问题

你遇到的问题核心是**second_frame是HomePage类内部的局部变量**,AddPage或者主应用的display方法无法直接访问它。要让这个框架在所有页面间可访问,我们只需要把它改成HomePage的实例属性,然后通过主应用的frames字典来获取它。

修改步骤

1. 将HomePage中的second_frame改为实例属性

在HomePage的__init__方法里,把原来的局部变量second_frame改为self.second_frame,这样HomePage的每个实例都会拥有这个属性,外部可以直接访问:

# 原代码
second_frame = tk.Frame(my_canvas)
# 修改后
self.second_frame = tk.Frame(my_canvas)
# 同时更新canvas的create_window参数,指向实例属性
my_canvas.create_window((0,0), window=self.second_frame, anchor="nw", width="10000")

2. 在display方法中使用HomePage实例的second_frame属性

在StudyFriendO的display方法里,所有引用second_frame的地方,都替换为frame2.second_frame(因为frame2是HomePage的实例)。另外,注意把要显示在滚动区域的控件都放在second_frame里(原来有些控件是直接放在HomePage主框架上的,这样滚动时不会跟着动,需要调整父容器):

def display(self, cont):
    frame2: HomePage = self.frames[HomePage]
    #blue box
    global x , y , c , d , e , f , g, h, i, j
    box_image = tk.PhotoImage(file='apple.png')
    # 父容器改为frame2.second_frame
    frame2.panel2 = tk.Label(frame2.second_frame, image=box_image, bg='#f7f6f6')
    frame2.panel2.image = box_image
    frame2.panel2.place(x=x, y=y)
    x=x
    y = y+260

    #assessment name
    frame2.n = tk.Label(frame2.second_frame, text="", bg="#e0f6fc", font=60)
    frame2.n.place(x=c, y=d, anchor='center')
    frame2.n.configure(text=self.shared_data['assessment'].get())
    c=c
    d=d+260

    #due date
    frame2.d = tk.Label(frame2.second_frame, text="Due:", bg="#e0f6fc", font=SMALL_FONT)
    frame2.d.place(x=c, y=e, anchor='center')
    c=c
    e=e+260

    #cal date
    frame2.c= tk.Label(frame2.second_frame, text="", bg="#e0f6fc", font=SMALL_FONT)
    frame2.c.place(x=c, y=f, anchor='center')
    frame2.c.configure(text=self.shared_data['due_date'].get())
    c=c
    f=f+260

    #no. of days
    plum = datetime.today().strftime("%m/%d/%y")
    date_format = "%m/%d/%y"
    a = datetime.strptime(plum, date_format)
    pear = self.shared_data['due_date'].get()
    b = datetime.strptime(pear, date_format)
    delta = b - a

    frame2.days = tk.Label(frame2.second_frame, font=LARGE_FONT, bg="#e0f6fc")
    frame2.days.place(x=c, y=g, anchor='center')
    c=c
    g=g+260
    frame2.days.config(text=delta.days)

    frame2.no_of_days=tk.Label(frame2.second_frame, text="DAYS",bg="#e0f6fc")
    frame2.no_of_days.place(x=c, y=h, anchor='center')
    c=c
    h=h+260

    #progress bar
    frame2.my_progress = ttk.Progressbar(frame2.second_frame, orient='horizontal', length=300, mode='determinate')
    frame2.my_progress.place(x=c, y=i, anchor='center')
    c=c
    i=i+260

    frame2.my_button = tk.Button(frame2.second_frame, text="Progress", command=self.step)
    frame2.my_button.place(x=c, y=j, anchor='center')
    c=c
    j=j+260

3. 额外优化:让date_label跟随滚动(可选)

如果你希望date_label也在滚动区域内,把它的父容器改成self.second_frame:

# HomePage里的date_label定义
self.date_label = tk.Label(self.second_frame, text="", bg="#c0e3ed")
self.date_label.place(relx=1, y=0, anchor='ne')

修改后的完整代码

import calendar
import tkinter as tk
from tkinter import ttk
from tkcalendar import *
from datetime import datetime
from datetime import date
from time import strftime
from datetime import timedelta, datetime, date

x = 200
y = 250
c = 460
d = 290
e = 325
f = 355
g = 390
h = 420
i = 460
j = 490

IMAGE_PATH = 'apple.png'
LARGE_FONT= ("Verdana", 24)
SMALL_FONT=("Verdana", 12)

class StudyFriendO(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        self.title("StudyFriendO") #naming window title
        self.geometry('850x830') #size of window

        container = tk.Frame(self)
        container.pack(side="top", fill="both", expand = True)

        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)

        #### ADDED
        self.shared_data = { # Variables shared by all pages.
            'name': tk.StringVar(value=''),
            'assessment': tk.StringVar(value=''),
            'due_date': tk.StringVar(value='')
        }

        self.frames = {}
        # 暂时保留你注释后的框架列表,如需恢复其他页面可重新加入
        for F in (HomePage, AddPage): 
            frame = F(container, self)
            self.frames[F] = frame
            frame.grid(row=0, column=0, sticky="nsew")

        self.show_frame(HomePage)

    def show_frame(self, cont):
        frame = self.frames[cont]
        frame.tkraise()

    def set_date(self, cont):
        frame2: HomePage = self.frames[HomePage]
        frame2.date_label.config(text= datetime.today().strftime("%B %d, %Y"))

    def getname(self, cont):
        frame2: HomePage = self.frames[HomePage]
        frame2.name_label.configure(text=self.shared_data['name'].get() + "'s Planner ")

    def step(self):
        self.frames[HomePage].my_progress['value']+= 5

    def display(self, cont):
        frame2: HomePage = self.frames[HomePage]
        #blue box
        global x , y , c , d , e , f , g, h, i, j
        box_image = tk.PhotoImage(file='apple.png')
        frame2.panel2 = tk.Label(frame2.second_frame, image=box_image, bg='#f7f6f6')
        frame2.panel2.image = box_image
        frame2.panel2.place(x=x, y=y)
        x=x
        y = y+260

        #assessment name
        frame2.n = tk.Label(frame2.second_frame, text="", bg="#e0f6fc", font=60)
        frame2.n.place(x=c, y=d, anchor='center')
        frame2.n.configure(text=self.shared_data['assessment'].get())
        c=c
        d=d+260

        #due date
        frame2.d = tk.Label(frame2.second_frame, text="Due:", bg="#e0f6fc", font=SMALL_FONT)
        frame2.d.place(x=c, y=e, anchor='center')
        c=c
        e=e+260

        #cal date
        frame2.c= tk.Label(frame2.second_frame, text="", bg="#e0f6fc", font=SMALL_FONT)
        frame2.c.place(x=c, y=f, anchor='center')
        frame2.c.configure(text=self.shared_data['due_date'].get())
        c=c
        f=f+260

        #no. of days
        plum = datetime.today().strftime("%m/%d/%y")
        date_format = "%m/%d/%y"
        a = datetime.strptime(plum, date_format)
        pear = self.shared_data['due_date'].get()
        b = datetime.strptime(pear, date_format)
        delta = b - a

        frame2.days = tk.Label(frame2.second_frame, font=LARGE_FONT, bg="#e0f6fc")
        frame2.days.place(x=c, y=g, anchor='center')
        c=c
        g=g+260
        frame2.days.config(text=delta.days)

        frame2.no_of_days=tk.Label(frame2.second_frame, text="DAYS",bg="#e0f6fc")
        frame2.no_of_days.place(x=c, y=h, anchor='center')
        c=c
        h=h+260

        #progress bar
        frame2.my_progress = ttk.Progressbar(frame2.second_frame, orient='horizontal', length=300, mode='determinate')
        frame2.my_progress.place(x=c, y=i, anchor='center')
        c=c
        i=i+260

        frame2.my_button = tk.Button(frame2.second_frame, text="Progress", command=self.step)
        frame2.my_button.place(x=c, y=j, anchor='center')
        c=c
        j=j+260

class HomePage(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)

        #### creating scrollbar ####
        #create a main frame
        main_frame = tk.Frame(self, bg='#f7f6f6')
        main_frame.pack(fill='both', expand=1)

        #create a canvas
        my_canvas = tk.Canvas(main_frame, bg='#f7f6f6')
        my_canvas.pack(side='left', fill='both', expand=1)

        #add a scroll bar to the canvas
        my_scrollbar = ttk.Scrollbar(main_frame, orient='vertical', command=my_canvas.yview)
        my_scrollbar.pack(side='right', fill='y')

        #create another frame inside the canvas - 改为实例属性
        self.second_frame = tk.Frame(my_canvas)
        # add that new frame to a window in the canvas
        my_canvas.create_window((0,0), window=self.second_frame, anchor="nw", width="10000")

        #height of Y
        tk.Label(main_frame, text="", fg="white", bg="#99cbd8").place(anchor='w', relheight=2, width='150')

        #width of x
        frame = tk.Frame(main_frame, bg="#c0e3ed", height=110)
        frame.place(anchor='n', relwidth=2)

        #LABEL WHICH DISPLAYS NAME FROM TEXT ENTRY
        self.name_label = tk.Label(main_frame, text="", font=("Arial Bold", 40), bg="#c0e3ed")
        self.name_label.place(relx=0.55, y=55, anchor='center')

        # 可选:将date_label移到second_frame里,跟随滚动
        self.date_label = tk.Label(self.second_frame, text="", bg="#c0e3ed")
        self.date_label.place(relx=1, y=0, anchor='ne')

        button1 = tk.Button(main_frame, height=2, width=10, bg="#e0f6fc", text="Home", command=lambda: controller.show_frame(HomePage))
        button1.place(x=35, y=145)

        button1 = tk.Button(main_frame, height=2, width=10, bg="#e0f6fc", text="To Do", command=lambda: controller.show_frame(ToDoPage))
        button1.place(x=35, y=225)

        button1 = tk.Button(main_frame, height=2, width=10, bg="#e0f6fc", text="Timetable", command=lambda: controller.show_frame(TimetablePage))
        button1.place(x=35, y=305)

        photo2 = tk.PhotoImage(file='apple.png')
        panel = tk.Label(main_frame, image = photo2)
        panel.image = photo2
        panel.place(x=35, y=400)

        #your upcoming assesments title
        title = tk.Label(main_frame, text="Your Upcoming Assesments", bg='#f7f6f6', font=LARGE_FONT)
        title.place(x=200, y=150)

        add_button = ttk.Button(main_frame, text="Add", command=lambda: controller.show_frame(AddPage))
        add_button.place(relx=0.8, rely=1, anchor='se')

        save_button = ttk.Button(main_frame, text="Save")
        save_button.place(relx=0.9, rely=1, anchor='se')

        clear_button = ttk.Button(main_frame, text="Clear")
        clear_button.place(relx=1, rely=1, anchor='se')

        self.configure(bg='#f7f6f6')

class AddPage(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)

        #width of x
        frame = tk.Frame(self, bg="#c0e3ed", height=110)
        frame.pack(fill="x")

        label = tk.Label(self, text="Add Assesment", font = LARGE_FONT, bg="#c0e3ed")
        label.place(relx=0.5, y=50, anchor='center')

        assessment_label = tk.Label(self, text="Enter assesment name:")
        assessment_label.place(relx=0.5, y=200, anchor='center')

        assessment_name = tk.Entry(self, textvariable=controller.shared_data['assessment'])
        assessment_name.place(relx=0.5, y=220, anchor='center')

        cal2 = Calendar(self, textvariable=controller.shared_data['due_date'], background="#e0f6fc", disabledbackground="white", bordercolor="light blue", headersbackground="light blue", normalbackground="#e0f6fc", foreground="black", normalforeground='black', headersforeground='white', selectmode="day", year=2021, month=8, day=9)
        cal2.place(relx=0.5, y=400, anchor='center')

        due_date = ttk.Button(self, text="Submit")
        due_date.place(relx=0.5, y=510, anchor='center')

        add_button = ttk.Button(self, text="Add Assesment", command=lambda: controller.display(HomePage))
        add_button.place(relx=0.5, y=560, anchor='center')

        button1 = ttk.Button(self, text="Home", command=lambda: controller.show_frame(HomePage))
        button1.pack()

# 如需恢复其他页面,取消以下注释并加入frames循环
# class StartPage(tk.Frame):
#     pass
# class ToDoPage(tk.Frame):
#     pass
# class TimetablePage(tk.Frame):
#     pass

app = StudyFriendO()
app.mainloop()

为什么这样能解决问题?

  • 把second_frame变成HomePage的实例属性后,我们可以通过主应用的frames字典获取HomePage的实例(self.frames[HomePage]),然后通过实例.second_frame访问到这个滚动子框架。
  • 所有需要显示在滚动区域的控件都要以second_frame作为父容器,这样当滚动条拖动时,这些控件才会跟着滚动。

内容的提问来源于stack exchange,提问作者Minbutt

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 20:27:53