Tkinter页面切换异常、定时器错误及代码优化求助
问题描述
- 现有两个可独立运行的Tkinter脚本
page1和menu_func,已实现page1调用打开menu_func,但关闭menu_func后,page1窗口虽保留却丢失图片等元素,期望点击返回按钮能恢复正常的page1页面。 - 同时遇到无关错误:
invalid command name "36335088time_now"
while executing
"36335088time_now"
("after" script)
- 希望获得代码简化优化建议。
原代码
page1代码
import tkinter as tk from tkinter import * from PIL import Image, ImageTk from time import strftime import subprocess import menu_func page1 = tk.Toplevel() width_value=page1.winfo_screenwidth() height_value=page1.winfo_screenheight() page1.geometry('%dx%d-0+0' % (width_value, height_value)) page1.resizable(False, False) BG1_img = Image.open('BG-BLK-1.png') BG2_img = BG1_img.resize((width_value,height_value)) BG3_img = ImageTk.PhotoImage(BG2_img) canvas = Canvas(page1) canvas.pack(expand=True, fill=tk.BOTH) limg = Label(canvas, image=BG3_img) limg.pack() SD1_img = Image.open('Bshut.png') SD2_img = ImageTk.PhotoImage(SD1_img) MU1_img = Image.open('./LIST.png') MU2_img = ImageTk.PhotoImage(MU1_img) def close(): page1.destroy() def time_now(): T_D = strftime('%A, %d %h %Y %H:%M:%S') show_date.config(text = T_D) show_date.after(1000, time_now) exit_button = Button(canvas,image=SD2_img, command=close,borderwidth='0',border='0',bg='#000000', highlightthickness='0') exit_button.place(x=1700, y=900) show_date = Label(canvas, font="Calibri, 46", fg="red", border='0',bg='#00090e') show_date.place(x=440, y=960) menu_button = Button(canvas, image=MU2_img, command= menu_func.menu, borderwidth=0, bg='#000000', highlightthickness=0) menu_button.place(x=50, y=900) time_now() page1.wm_attributes('-fullscreen', 'True') page1.mainloop()
menu_func代码
import tkinter as tk from tkinter import * from tkinter.messagebox import showinfo from PIL import Image, ImageTk from time import strftime def menu(): pageM = tk.Toplevel() w_v=pageM.winfo_screenwidth() h_v=pageM.winfo_screenheight() pageM.geometry('%dx%d-0+0' % (w_v, h_v)) pageM.resizable(False, False) BG1m_img = Image.open('BG-BLK-1.png') BG2m_img = BG1m_img.resize((w_v,h_v)) BG3m_img = ImageTk.PhotoImage(BG2m_img) canvas = Canvas(pageM) butt_W = int(w_v/28) butt_H = int(h_v/31) img_W = int(w_v/8) img_H = int(h_v/4) canvas = Canvas(pageM) canvas.pack(expand=True, fill=tk.BOTH) limg = Label(canvas, image=BG3m_img) limg.pack() def time_now(): T_D = strftime('%H:%M:%S') show_time.config(text = T_D) show_time.after(1000, time_now) show_time = Label(canvas, font="Calibri, 46", fg="red", border='0',bg='#003a52') show_time.place(x=int(w_v/2.3), y=int(h_v/50)) def call_COM(): showinfo(title='Information',message='phone Button clicked!') call_img = Image.open('CALL.png') cal1_img = call_img.resize((img_W,img_H)) cal2_img = ImageTk.PhotoImage(cal1_img) button1=tk.Button(canvas, image=(cal2_img),borderwidth=0,bg='#000000', highlightthickness=0) button1.place(x=int(w_v/5*1-(img_W)+50),y=int(h_v/3*0.8-(img_H/2))) def map_COM(): exec(open("mapscreen.py").read()) c1_img = Image.open('comp.png') c2_img = c1_img.resize((img_W,img_H)) c3_img = ImageTk.PhotoImage(c2_img) button2=tk.Button(canvas, image=c3_img,command=map_COM,borderwidth=0,bg='#000000', highlightthickness=0) button2.place(x=int(w_v/5*2-(img_W/1.2)+50),y=int(h_v/3*0.8-(img_H/2))) def user_COM(): showinfo(title='Information',message='user Button clicked!') u1_img = Image.open('user.png') u2_img = u1_img.resize((img_W,img_H)) u3_img = ImageTk.PhotoImage(u2_img) button3=tk.Button(canvas, image=u3_img,borderwidth=0,bg='#000000', highlightthickness=0) button3.place(x=int(w_v/5*3-(img_W/1.5)+50),y=int(h_v/3*0.8-(img_H/2))) def set_COM(): showinfo(title='Information',message='settings Button clicked!') s1_img = Image.open('settings.png') s2_img = s1_img.resize((img_W,img_H)) s3_img = ImageTk.PhotoImage(s2_img) button4=tk.Button(canvas, image=s3_img,borderwidth=0,bg='#000000', highlightthickness=0) button4.place(x=int(w_v/5*4-(img_W/2)+50),y=int(h_v/3*0.8-(img_H/2))) def bro_COM(): exec(open('browser.py').read()) b1_img = Image.open('brows.png') b2_img = b1_img.resize((img_W,img_H)) b3_img = ImageTk.PhotoImage(b2_img) button5=tk.Button(canvas, image=b3_img,command=bro_COM,borderwidth=0,bg='#000000', highlightthickness=0) button5.place(x=int(w_v/5*1-(img_W)+50),y=int(h_v/3*2.2-(img_H/1.5))) def mus_COM(): showinfo(title='Information',message='Music Button clicked!') m1_img = Image.open('music.png') m2_img = m1_img.resize((img_W,img_H)) m3_img = ImageTk.PhotoImage(m2_img) button6=tk.Button(canvas, image=m3_img,borderwidth=0,bg='#000000', highlightthickness=0) button6.place(x=int(w_v/5*2-(img_W/1.2)+50),y=int(h_v/3*2.2-(img_H/1.5))) def lite_COMM(): showinfo(title='Information',message='hardware Button clicked!') t1_img = Image.open('Torch.png') t2_img = t1_img.resize((img_W,img_H)) t3_img = ImageTk.PhotoImage(t2_img) button7=tk.Button(canvas, image=t3_img,borderwidth=0,bg='#000000', highlightthickness=0) button7.place(x=int(w_v/5*3-(img_W/1.5)+50),y=int(h_v/3*2.2-(img_H/1.5))) def back_COM(): pageM.quit() SD1_img = Image.open('home.png') SD2_img = SD1_img.resize((img_W,img_H)) SD3_img = ImageTk.PhotoImage(SD2_img) button8=tk.Button(canvas, image=SD3_img,command=back_COM,borderwidth=0,bg='#000000', highlightthickness=0) button8.place(x=int(w_v/5*4-(img_W/2)+50),y=int(h_v/3*2.2-(img_H/1.5))) time_now() pageM.mainloop()
问题解决
1. 修复page1窗口元素丢失问题
问题根源:menu_func中调用pageM.mainloop()会嵌套主循环,导致返回后page1的事件循环异常;同时图片对象因局部变量被垃圾回收,导致显示丢失。
修改方案:
menu_func的menu函数接收page1作为父窗口参数,设置窗口为父窗口的临时子窗口:def menu(parent): pageM = tk.Toplevel(parent) pageM.transient(parent) # 绑定到父窗口,关闭后不影响主循环 # 去掉最后一行的pageM.mainloop()page1中调用menu时传入父窗口:menu_button = Button(canvas, image=MU2_img, command=lambda: menu_func.menu(page1), borderwidth=0, bg='#000000', highlightthickness=0)- 给所有图片添加全局/容器引用(比如在
page1中把图片保存到self.images列表,menu_func中保存到pageM.images),避免被垃圾回收。
2. 修复"invalid command name"错误
错误原因:menu_func关闭后,其内部time_now的定时器仍在执行,但对应控件已销毁。解决方法:
- 启动定时器时保存ID,关闭窗口时取消定时器:
# 在menu_func的time_now定义后 time_now_id = show_time.after(1000, time_now) # 修改back_COM函数 def back_COM(): show_time.after_cancel(time_now_id) pageM.destroy() # 用destroy替代quit,避免终止主循环
代码优化建议
1. 统一导入方式
不要同时使用import tkinter as tk和from tkinter import *,推荐用import tkinter as tk,避免命名冲突。
2. 封装重复逻辑
把图片加载、缩放封装成函数,减少重复代码:
def load_resize_image(path, size=None): img = Image.open(path) if size: img = img.resize(size) return ImageTk.PhotoImage(img)
3. 面向对象重构
把每个页面封装成类,方便管理控件和状态:
class Page1(tk.Tk): def __init__(self): super().__init__() self.setup_window() self.load_images() self.create_widgets() self.start_clock() def setup_window(self): # 窗口尺寸、全屏设置等 pass # 其余方法...
4. 常量集中管理
把图片路径、字体、颜色等常量集中定义,方便修改:
# 常量定义 BG_PATH = "BG-BLK-1.png" FONT_CALIBRI_46 = ("Calibri", 46) COLOR_RED = "red" COLOR_BLACK = "#000000"
5. 替换exec(open(...).read())
这种方式易导致重复定义和资源泄漏,推荐将其他脚本封装成函数,通过导入模块调用:
# mapscreen.py中封装函数 def run(): # 原mapscreen.py的逻辑 pass # menu_func中调用 import mapscreen def map_COM(): mapscreen.run()
内容的提问来源于stack exchange,提问作者George Durell
相关产品推荐
相关产品推荐

