在tkinter中嵌入的matplotlib在关闭窗口时无法退出主循环

0 投票
1 回答
60 浏览
提问于 2025-04-13 16:54

我把我的代码简化成了这个最小可复现的例子:

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 正在运行,因此你的其他代码会被阻塞,直到你关闭窗口!

撰写回答