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

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()
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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 19:45:31