在tkinter中嵌入的matplotlib在关闭窗口时无法退出主循环
我把我的代码简化成了这个最小可复现的例子:
import matplotlib.pyplot as plt
from tkinter import Tk
from tkinter import Tk, Frame
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
Dates=["a","b","c","d"]
Numbers=[1,4,2,6]
def test():
fig, ax = plt.subplots(figsize=(10,7))
ax.plot(Dates, Numbers, linestyle='-', color='gray', alpha=0.5)
root = Tk()
root.title("Test")
frame = Frame(root)
frame.pack(fill='both', expand=True)
canvas = FigureCanvasTkAgg(fig, master=frame)
canvas.draw()
canvas.get_tk_widget().pack(fill='both', expand=True)
def f_test():
print("HI")
f_test()
root.mainloop()
test()
我遇到的问题是,每次我调用这个函数时,它开始运行但从来没有结束。即使我关闭了Tk窗口,它还是一直在运行。我也试过用 'winfo_exists()'
,但就是不管用。
我把 'f_test'
放在 'test()'
里面,因为实际上我有两个函数只在 'test()'
里面使用,所以我把它们定义在里面。
1 个回答
1
正如我在评论中所猜测的,这个问题是因为 tkinter 的 mainloop
阻塞了,而在你的情况下它并没有被正确退出。
mainloop 是一个事件处理器。简单来说,它就是一个不断更新你图形界面的循环。所以一旦你进入这个循环,后面的代码就再也不会执行了。想了解更多,可以参考 这篇文章。
根据 matplotlib.org 的这个教程,我实现了一个关闭程序的例程 _on_closing
,用来停止阻塞的 mainloop
,并正确地销毁 Tk
实例。这个例程会在你关闭 tkinter 窗口时被 WM_DELETE_WINDOW
事件触发。
import matplotlib.pyplot as plt
from tkinter import Tk, Frame
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
Dates = ["a", "b", "c", "d"]
Numbers = [1, 4, 2, 6]
def test():
fig, ax = plt.subplots(figsize=(10, 7))
ax.plot(Dates, Numbers, linestyle='-', color='gray', alpha=0.5)
root = Tk()
root.title("Test")
frame = Frame(root)
frame.pack(fill='both', expand=True)
canvas = FigureCanvasTkAgg(fig, master=frame)
canvas.draw()
canvas.get_tk_widget().pack(fill='both', expand=True)
def f_test():
print("HI")
def _on_closing():
root.quit() # stops mainloop
root.destroy() # this is necessary on Windows to prevent Fatal Python Error: PyEval_RestoreThread: NULL tstate
root.protocol("WM_DELETE_WINDOW", _on_closing) # bind closing routine
f_test()
root.mainloop()
test()
请注意,当 tkinter 窗口打开时,mainloop
正在运行,因此你的其他代码会被阻塞,直到你关闭窗口!