Tkinter二次打开子窗口后焦点切换报错问题求助
问题分析与解决方案
核心问题原因
- 全局列表未清空:
win2_lstHandle作为全局变量,每次打开子窗口都会追加输入框控件引用。关闭子窗口后,旧控件的引用仍留在列表中,再次打开子窗口时,焦点切换函数会尝试访问已销毁的控件,触发_tkinter.TclError。 - 错误使用
Tk()创建子窗口:Tkinter程序应仅存在一个主Tk实例,用Tk()创建子窗口会生成独立的Tcl解释器,引发焦点管理混乱,导致跨窗口切换焦点的问题。 - 焦点切换依赖全局状态:子窗口的焦点切换逻辑依赖全局列表,多个子窗口实例共享同一列表,导致焦点跳转时遍历所有历史控件。
修正后的代码
from tkinter import * from tkinter import ttk def o_downKey(event): o_handle = event.widget.focus_get() o_lstHandle[(o_lstHandle.index(o_handle) + 1)%len(o_lstHandle)].focus() def o_upKey(event): o_handle = event.widget.focus_get() o_lstHandle[(o_lstHandle.index(o_handle) - 1)%len(o_lstHandle)].focus() def new_win(): # 用Toplevel创建子窗口,依附于主窗口 win2 = Toplevel() win2.title('Расчет квадратуры') win2.geometry('300x185+600+190') win2.resizable(0,0) # 子窗口控件列表设为局部变量,每个窗口独立维护 win2_lstHandle = [] def win2_downKey(event): win2_handle = event.widget.focus_get() win2_lstHandle[(win2_lstHandle.index(win2_handle) + 1)%len(win2_lstHandle)].focus() def win2_upKey(event): win2_handle = event.widget.focus_get() win2_lstHandle[(win2_lstHandle.index(win2_handle) - 1)%len(win2_lstHandle)].focus() lbl_up = Label(win2, text = 'win2 label', font = 'normal, 13') lbl_up.place(x=5, y=7) lbl_kk = Label(win2, text = 'win2 labl', font = 'normal, 13') lbl_kk.place(x=5, y=34) ent_up = Entry(win2, font = 'normal, 13',borderwidth=3, relief="ridge") win2_lstHandle.append(ent_up) ent_up.bind('<Left>', win2_downKey) ent_up.bind('<Right>', win2_upKey) ent_up.bind('<Return>', win2_upKey) ent_up.place(width=85, x=127, y=6) ent_kk = Entry(win2, width = 18, font = 'normal, 13',borderwidth=3, relief="ridge") win2_lstHandle.append(ent_kk) ent_kk.bind('<Left>', win2_downKey) ent_kk.bind('<Right>', win2_upKey) ent_kk.bind('<Return>', win2_upKey) ent_kk.place(x=127, y=34) ent_up1 = Entry(win2, font = 'normal, 13',borderwidth=3, relief="ridge") win2_lstHandle.append(ent_up1) ent_up1.bind('<Left>', win2_downKey) ent_up1.bind('<Right>', win2_upKey) ent_up1.bind('<Return>', win2_upKey) ent_up1.place(width=85, x=212, y=6) # 关闭子窗口时清空列表,避免残留引用 def on_close(): win2_lstHandle.clear() win2.destroy() win2.protocol("WM_DELETE_WINDOW", on_close) win = Tk() win.title("CFS") win.geometry('500x310+500+200') win.resizable(0, 0) o_lstHandle = [] mainmenu = Menu(win) win.config(menu=mainmenu) filemenu = Menu(mainmenu, tearoff=0) filemenu.add_command(label="1", command = new_win) filemenu_1 = Menu(mainmenu, tearoff=0) filemenu_1.add_command(label="1") filemenu_1.add_command(label="2") filemenu_1.add_command(label="3") mainmenu.add_cascade(label="command", menu=filemenu) mainmenu.add_cascade(label = 'command 2', menu = filemenu_1) frame = ttk.Frame(win, relief="groove", borderwidth = 0) frame.pack(expand=1, fill='both') tab_control = ttk.Notebook(frame) tab1 = ttk.Frame(relief="groove", borderwidth = 1) tab3 = ttk.Frame(relief="groove", borderwidth = 1) tab4 = ttk.Frame(relief="groove", borderwidth = 1) s = ttk.Style() s.configure('TNotebook.Tab', font='normal, 10') tab_control.add(tab1, text='tab1') tab_control.add(tab3, text='tab2') tab_control.add(tab4, text='tab3') tab_control.pack(expand=1, fill='both', padx=1,pady=1) o_ent1 = Entry(tab1, width=8, font = 'normal, 13',borderwidth=3, relief="ridge") o_lstHandle.append(o_ent1) o_ent1.bind('<Down>', o_downKey) o_ent1.bind('<Up>', o_upKey) o_ent1.bind('<Return>', o_downKey) o_ent1.place(x=115,y=15) o_ent2 = Entry(tab1, width=8, font = 'normal, 13',borderwidth=3, relief="ridge") o_lstHandle.append(o_ent2) o_ent2.bind('<Down>', o_downKey) o_ent2.bind('<Up>', o_upKey) o_ent2.bind('<Return>', o_downKey) o_ent2.place(x=115,y=41) o_ent3 = Entry(tab1, width=8, font = 'normal, 13',borderwidth=3, relief="ridge") o_lstHandle.append(o_ent3) o_ent3.bind('<Down>', o_downKey) o_ent3.bind('<Up>', o_upKey) o_ent3.bind('<Return>', o_downKey) o_ent3.place(x=115,y=67) o_ent4 = Entry(tab1, width=8, font = 'normal, 13',borderwidth=3, relief="ridge") o_lstHandle.append(o_ent4) o_ent4.bind('<Down>', o_downKey) o_ent4.bind('<Up>', o_upKey) o_ent4.bind('<Return>', o_downKey) o_ent4.place(x=115,y=93) o_ent5 = Entry(tab1, width=8, font = 'normal, 13',borderwidth=3, relief="ridge") o_lstHandle.append(o_ent5) o_ent5.bind('<Down>', o_downKey) o_ent5.bind('<Up>', o_upKey) o_ent5.bind('<Return>', o_downKey) o_ent5.place(x=115,y=119) o_ent6 = Entry(tab1, width=8, font = 'normal, 13',borderwidth=3, relief="ridge") o_lstHandle.append(o_ent6) o_ent6.bind('<Down>', o_downKey) o_ent6.bind('<Up>', o_upKey) o_ent6.bind('<Return>', o_downKey) o_ent6.place(x=115,y=145) o_lb1 = Label(tab1, text='label 1', font = 'normal, 13') o_lb1.place(x=15,y=15) o_lb2 = Label(tab1, text='label 2', font = 'normal, 13') o_lb2.place(x=15,y=41) o_lb3 = Label(tab1, text='label 3', font = 'normal, 13') o_lb3.place(x=15,y=67) o_lb4 = Label(tab1, text='label 4', font = 'normal, 13') o_lb4.place(x=15,y=93) o_lb5 = Label(tab1, text='label 5', font = 'normal, 13') o_lb5.place(x=15,y=119) o_lb6 = Label(tab1, text='label 6', font = 'normal, 13') o_lb6.place(x=15,y=145) otkr_knop = Button(tab1, text = 'Button 1', font = 'normal, 13', borderwidth=3, relief="ridge") otkr_knop.place(x=10,y=230) o_ochist = Button(tab1, text = 'Button 2', font = 'normal, 13', borderwidth=3, relief="ridge") o_ochist.place(x=110,y=230) canvas = Canvas(tab1, bg="white", width=280, height=258, relief=GROOVE, bd=3) canvas.pack(anchor = SE) tpk1 = Label(tab1, text = 'text"', font = 'Arial 9 bold roman', fg='blue') tpk1.place(x=402,y=264) mytex = canvas.create_text(150, 135, text="") win.mainloop()
关键修正点
- 替换
Tk()为Toplevel():子窗口依附于主窗口,共享同一个Tcl解释器,解决焦点管理混乱问题。 - 局部化控件列表:每个子窗口拥有独立的
win2_lstHandle,关闭后列表随函数上下文回收,不会残留已销毁控件的引用。 - 修复字体拼写错误:将
noraml改为normal,避免字体加载异常。 - 添加窗口关闭回调:明确清空控件列表,进一步避免潜在的内存泄漏。
内容的提问来源于stack exchange,提问作者shbidibom
相关产品推荐
相关产品推荐

