Matplotlib and it's connection with tkinter
<p>I have a question, which may not be even related to the modules. </p>

<p>In the code below, there is a function update, which will create a canvas by matplotlib and assign it to the related frame from tkinter. Then it create an event handler Cursor, which should print the position of the mouse into the console. But it does not. However, everything works just fine, if you delete method update and use the lines for creating figure, cursor and connection just in the body of the module.</p>

<p>What am I missing? I guess it is a basic knowledge of Python, the visibility and passing the right instance, I don't know. </p>

<pre><code>import matplotlib.pyplot as plt
from tkinter import *
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

class Cursor (object):
    def __init__ (self, fig):
        self.fig = fig
        print ("initializing")

    def mouse_move (self, event):
        if not event.inaxes:
            return 
        x, y = event.xdata, event.ydata
        print (x, y)

    def connect (self):
        print ("Connecting")
        self.conmove = self.fig.canvas.mpl_connect ('motion_notify_event', self.mouse_move)

def pyplot_f ():
    fig = plt.figure(figsize=(6,4), dpi=100)
    axes = fig.add_subplot (111)
    axes.plot([1,2,3,4], [1,2,3,4])

    Canvas = FigureCanvasTkAgg (fig, master=frame_output)

    canvas = Canvas.get_tk_widget()
    canvas.grid(row=0,column=0, padx=5, pady=5, sticky="nesw")

    return fig

w_width = 1000
w_height = 600
root = Tk()
root.resizable(0,0)

frame_output = Frame (root, bg="", relief=SUNKEN, width=w_width*0.8, height=w_height*0.9)
frame_output.grid(row=0, column=0, padx=20, pady=20, sticky=W+N+E+S)
frame_input = Frame (root, bg="", relief=RAISED,width=w_width*0.2, height=w_height*0.9)
frame_input.grid(row=0, column=1, padx=20, pady=20, sticky=W+N+E+S)

def update ():
    fig = pyplot_f()
    cursor = Cursor(fig)
    cursor.connect()

def on_closing():
    print ("Exiting")
    root.quit()

update()
root.protocol("WM_DELETE_WINDOW", on_closing)
root.mainloop()
</code></pre>

<p>Your problem appears to be one about variable scope and lifetime.</p>

<p>When your <code>update()</code> function ends, the variables <code>fig</code> and <code>cursor</code> declared within it go out of scope.  The figure and cursor objects created within your <code>update()</code> have no further references pointing to them, and so they end up being garbage-collected.  Your <code>update()</code> function is successfully creating the figure and the cursor, but it isn't preventing them from being deleted again.</p>

<p>When you move the three lines within <code>update()</code> to the body of the module, the variables <code>fig</code> and <code>cursor</code> then remain in scope and don't get garbage-collected until the program ends.  Hence your figure and cursor are created and not immediately garbage-collected.</p>

<p>The simplest way to fix this is for the <code>update()</code> function to return the cursor, and then keep that in module scope:</p>

<pre><code>def update ():
    fig = pyplot_f()
    cursor = Cursor(fig)
    cursor.connect()
    return cursor

# ...

cursor = update()
</code></pre>

<p>This prevents the cursor from being garbage-collected, and as the cursor has a reference to the figure, the figure won't get garbage-collected either.</p>

python matplotlib tkinter