root.destroy()和root.quit()有什么区别?
在Python中使用tkinter
时,root.destroy()
和root.quit()
在关闭主窗口时有什么区别呢?
这两者哪个更好呢?有没有哪个能释放资源而另一个不能呢?
4 个回答
tkinter.Tk 的 "quit" 方法可以退出 "mainloop" 事件处理,而 "destroy" 方法则会销毁所有嵌入的控件,然后才退出 "mainloop"。那么,"destroy" 是不是更好呢?其实,有时候并不是。如果 "destroy" 没能成功销毁所有控件,"mainloop" 就不会退出,Python 就会卡住。在脚本结束时,让 Python 有序地关闭一切,可能会更好。
举个例子,如果你在 Tkinter 窗口中嵌入一个 Matplotlib 图表,这样做是有用的,因为 Matplotlib 自带的控件用起来有点麻烦。不幸的是,如果你尝试通过点击标题栏的 "X" 来关闭窗口,窗口虽然关闭了,但 Python 仍然在运行。如果这个脚本是从终端启动的,你可能得不停按 Ctrl-C 好几分钟才能恢复提示符。原因是窗口关闭事件绑定到了 "destroy",而 "destroy" 并不会销毁 Matplotlib 对象,导致它们变成孤儿。
解决办法是把窗口关闭事件绑定到 "quit"。但是……如果脚本是在像 IDLE 这样的 Tkinter IDE 中启动的,那又会出现新问题,因为 IDLE 会让 Tkinter 一直运行,这样窗口就无法关闭。所以这时需要在 mainloop 之后添加 "destroy"。最后,一切就正常了。
下面是一个简单的例子,展示了一个可以通过 Tkinter 按钮翻转的 Matplotlib 图表。这个窗口可以顺利关闭。但如果窗口关闭事件绑定的是 "destroy" 而不是 "quit",那么 Python 进程就会卡住。
#!/usr/bin/env python3
import tkinter as tk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.pyplot as plt
root = tk.Tk()
data, = plt.plot([0,5,3,4,-5,3])
canvas = FigureCanvasTkAgg(plt.gcf(), master=root)
invert = lambda: (data.set_ydata(-data.get_ydata()), canvas.draw())
tk.Button(master=root, text="Invert", command=invert).pack()
canvas.get_tk_widget().pack(fill=tk.BOTH, expand=1)
root.protocol("WM_DELETE_WINDOW", root.quit)
root.mainloop()
root.destroy()
补充:我还想说,如果把窗口关闭事件同时绑定到 两个 方法,你就可以避免在 "mainloop" 后面添加一行代码,这样做在某些情况下可能会更方便:
root.protocol("WM_DELETE_WINDOW", lambda: (root.quit(), root.destroy()))
root.quit()
这个命令会让主循环停止运行。虽然主循环停止了,但解释器和所有的控件(比如按钮、输入框等)依然存在。如果你调用了这个函数,那么在 root.mainloop()
之后的代码会继续执行,并且这些代码可以和控件进行互动(比如从输入框中获取值)。
而调用 root.destroy()
则会把所有的控件都删除,并且也会停止主循环。虽然在 root.mainloop()
之后的代码还会运行,但如果你试图访问任何控件(比如获取输入框中的值),就会失败,因为这些控件已经不存在了。
quit()
是用来停止TCL解释器的。在大多数情况下,这正是你想要的,因为你的Tkinter应用程序也会随之停止。不过,如果你是从idle(一个Python的开发环境)中调用你的应用,这就可能会出问题。因为idle本身也是一个Tkinter应用,所以如果你在你的应用中调用了quit()
,TCL解释器被终止后,idle也会被关闭(或者变得混乱)。
destroy()
只是结束主循环并删除所有的控件。所以如果你是从另一个Tkinter应用中调用你的应用,或者你有多个主循环,使用这个方法似乎更安全。