如何通过tkinter按钮触发的内层函数终止外层主函数并处理GUI关闭报错
解决方案
1. 核心问题对应解法
你遇到的两个核心问题无需用类或抛异常就能解决:
- 内层按钮触发函数无法终止外层函数:tkinter自带
root.quit()方法可以直接退出root.mainloop()循环,直接执行外层函数后续逻辑,配合全局标记位即可完全符合你的需求 - 调用
root.destroy()触发iconphoto报错:root.destroy()会直接销毁root对象,后续代码访问root属性就会触发报错,替换为root.withdraw()隐藏窗口即可,全程只保留一个Tk实例无需重复创建
2. 具体修改点
- 给窗口绑定
WM_DELETE_WINDOW关闭事件,触发时直接弹出确认框 - 输入合规后调用
root.quit()退出主循环,不需要销毁窗口 - 移除重复创建Tk实例的逻辑,全程只初始化一次root,避免重复调用iconphoto报错
3. 修改后完整可运行代码
import tkinter as tk from tkinter import messagebox as msgbox from PIL import ImageTk lst = [] # 全局标记位:标记用户是否选择直接退出程序 exit_flag = False black = '#%02x%02x%02x' % (50, 50, 50) white = '#%02x%02x%02x' % (240, 240, 240) red = '#%02x%02x%02x' % (255, 80, 80) yellow = '#%02x%02x%02x' % (255, 220, 80) green = '#%02x%02x%02x' % (120, 255, 150) blue = '#%02x%02x%02x' % (0, 220, 240) purple = '#%02x%02x%02x' % (120, 80, 255) window_icon = 'icon.jpg' # 全程只初始化一次root,避免重复创建报错 root = tk.Tk() root.withdraw() root.iconphoto(True, ImageTk.PhotoImage(file=window_icon)) root.attributes("-topmost", True) def root_win(): global lst, exit_flag lst = [] exit_flag = False root.deiconify() w_root = 500 h_root = 320 pos_right = round(root.winfo_screenwidth() / 2 - w_root / 2) pos_down = round(root.winfo_screenheight() / 2 - h_root / 2) root.title('Enter the values') root.resizable(width=False, height=False) root.geometry('{}x{}+{}+{}'.format(w_root, h_root, pos_right, pos_down)) root.configure(bg=white) # 清空窗口旧组件,避免重复创建 for widget in root.winfo_children(): widget.destroy() tk.Label(root, text='Enter the values', font=('bold', 30), bg=white, fg=black).place(x=70, y=20) tk.Label(root, text='Enter width here:', font=('bold', 15), bg=white, fg=black).place(x=50, y=100) tk.Label(root, text='Enter height here:', font=('bold', 15), bg=white, fg=black).place(x=50, y=140) val1 = tk.Entry(root, bd=0, font=('bold', 15)) val1.place(x=280, y=102, width=170) val2 = tk.Entry(root, bd=0, font=('bold', 15)) val2.place(x=280, y=142, width=170) lbl = tk.Label(root, text='Min: 5, Max: 100', font=('bold', 15), bg=white, fg=purple) lbl.place(x=170, y=260) def enter(): global lst if val1.get() == '' and val2.get() == '': lbl.config(text='Please enter width and height!') lbl.place(x=80, y=260) elif val1.get() == '': lbl.config(text='Please enter a width!') lbl.place(x=145, y=260) elif val2.get() == '': lbl.config(text='Please enter a height!') lbl.place(x=140, y=260) else: wid, hit = 0, 0 try: wid = round(float(val1.get())) hit = round(float(val2.get())) except: lbl.config(text='Please enter value from 5 to 100!') lbl.place(x=70, y=260) return if not 5 <= wid <= 100 or not 5 <= hit <= 100: lbl.config(text='Please enter value from 5 to 100!') lbl.place(x=70, y=260) return lbl.config(text='INPUT ACCEPTED !!!!') lbl.place(x=130, y=260) lst = [wid, hit] # 输入合规,隐藏窗口退出主循环 root.withdraw() root.quit() def clr(): val1.delete(0, 'end') val2.delete(0, 'end') lbl.config(text='Min: 5, Max: 100') lbl.place(x=170, y=260) def on_close(): global exit_flag action = msgbox.askyesno(title='Exit prompt', message='Are you sure you want to exit?\nYes: Exit\nNo: Restart\n', icon='warning', default='no') if action: exit_flag = True root.withdraw() root.quit() else: # 选择重启,清空当前输入 clr() # 绑定窗口关闭事件 root.protocol("WM_DELETE_WINDOW", on_close) enter = tk.Button(root, text='Enter', font=('bold', 15), bd=0, fg=black, bg=green, activebackground=blue, command=enter) enter.place(x=300, y=200) enter.configure(width=8) clear = tk.Button(root, text='Clear', font=('bold', 15), bd=0, fg=black, bg=red, activebackground=yellow, command=clr) clear.place(x=100, y=200) clear.configure(width=8) root.mainloop() while True: root_win() # 用户选择直接退出则终止程序 if exit_flag: break # 拿到合规输入则终止循环 if lst: break print(lst)
内容的提问来源于stack exchange,提问作者Ishan Jindal
相关产品推荐
相关产品推荐

