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
相关产品推荐
相关产品推荐

