简体   繁体   中英

Why doesn't mpl_connect work when called in the __init__ of a class?

I am working on making an interactive pyplot graph with "fig.canvas.mpl_connect(event,function)" when keys or mouse are pressed. It works as expected. However, to make it easily importable I need to condense the functions into a class. The mpl_connect function would be called in the __init__ function of the class. But this makes it fail to work. Only when mpl_connect is called outside the class in reference to the functions inside the class, it does work.

Of course I've tried to call it without classes and this works as expected. And to show that the problem occurs inside the __init__ function of the class, I called mpl_connect outside the class in reference to the function inside the class.

Here's a condensed, ducktesting version of what I try to achieve. It's a simple event handler which makes the console print "yes" when you click on the pyplot graph with your mouse. :

->WORKS, though no classes! Unreliable with importing to other programs.

import matplotlib.pyplot as plt
def abc(event):
   print("yes")
fig, ax = plt.subplots()
fig.canvas.mpl_connect("button_press_event",abc)
plt.show()

->DOESN'T WORK, doesn't print "yes" (even if self.fig is used.)

import matplotlib.pyplot as plt
class test():
    def __init__(self):
        fig, ax = plt.subplots()
        fig.canvas.mpl_connect("button_press_event",self.abc)
    def abc(self, event):
        print("Yes")
test()
plt.show()

->DOES WORK, now with a class storing the function, but references to the class should be used and it doesn't look nice :

import matplotlib.pyplot as plt
class test():
    def __init__(self):
        self.FIG, ax = plt.subplots()
    def abc(self, event):
        print("Yes")  
A = test()
A.FIG.canvas.mpl_connect("button_press_event",A.abc)
plt.show()

I expect it to print "yes" when I click on the graph when the mpl_connect function is called during the initialisation of the class, the normal, logical pythonic way. I believe it's a bug. Thank you.

EDIT, fixed by adding weak reference.

import matplotlib.pyplot as plt
class test():
    def __init__(self):
        self.fig, ax = plt.subplots()
        self.cid_abc = self.fig.canvas.mpl_connect("button_press_event",self.abc)
    def abc(self, event):
        print("Yes")
mytest = test()
plt.show()

You will need to retain a reference to the callback id that is created by mpl_connect . As the CallbackRegistry doc string formulates:

In practice, one should always disconnect all callbacks when they are no longer needed to avoid dangling references (and thus memory leaks). However, real code in Matplotlib rarely does so, and due to its design, it is rather difficult to place this kind of code. To get around this, and prevent this class of memory leaks, we instead store weak references to bound methods only, so when the destination object needs to die, the CallbackRegistry won't keep it alive .

In other words, you need to assign the return of mpl_connect to an instance variable, like

self.cid = fig.canvas.mpl_connect("button_press_event", self.abc)

similar to how you find it in the event handling guide .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM