Search code examples
pythonmatplotlibtkinter

matplotlib embedded in tkinter won't exit mainloop uppon window closing


I boiled my code down into this minimal reproducible example:

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()

The problem im having is that whenever I call the function, it starts running but never ends. It keeps running even though I close the Tk window. I have laso tried with 'winfo_exists()' But it just wont work.

I have 'f_test' inside 'test()' because irl I have 2 functions that are only used inside 'test()' so i've defined them inside it.


Solution

  • As I suspected in the comments this is due to the blocking tkinter mainloop which is never properly exited in your case.

    The mainloop is an eventhandler. That means its basically an endless loop updating your GUI. So once you enter it. Everything following in your code will never be reached. See this post for reference.

    Following this tutorial from matplotlib.org I implemented a closing routine _on_closing to stop the blocking mainloop and to destroy the Tk instance properly. This routine is triggert by the WM_DELETE_WINDOW event when closing the tkinter 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()
    
    

    Please note also that while the tkinter window is open mainloop is running and therefore the rest of your code is blocked until you close the window!