Search code examples
python-3.xmatplotlibmatplotlib-widget

Pick event class wrapped into a function cannot work


Please see the following code:

class PickCursor(object):
    def __init__(self, collection, alpha_other=0.3, tolerance=5):
        self.collection = collection
        self.alpha_other = alpha_other

        self.pts= collection.get_offsets()
        self.num_pts = len(self.pts)
        # Need to set a tolerance to make it take effect
        self.collection.set_picker(tolerance)

        # Ensure that we have separate colors for each object
        self.fc = collection.get_facecolors()
        if len(self.fc) == 0:
            raise ValueError('Collection must have a facecolor')
        elif len(self.fc) == 1:
            self.fc = np.tile(self.fc, (self.num_pts, 1))
        # self.fc is a 2d array every row follows [r, g, b, a]

        self.ind = None
        self.point_selected = None
        self.canvas = ax.figure.canvas
        self.canvas.mpl_connect('pick_event', self.onselect)

    def onselect(self, event):
        self.ind = event.ind[0]
        self.point_selected = self.pts[self.ind]
        # Change alpha of other points
        self.fc[:, -1] = self.alpha_other
        self.fc[self.ind, -1] = 1
        self.collection.set_facecolors(self.fc)
        self.canvas.draw_idle()

fig, ax = plt.subplots(1, 1, figsize=(8, 8))
x = np.arange(0, 10)
y = np.arange(0, 10)
scat = ax.scatter(x, y, s=15)
PickCursor(scat)

The above code works! Basically, it will make points unselected transparent. However, if I wrap the code into a function like this:

def func():
    fig, ax = plt.subplots(1, 1, figsize=(8, 8))
    x = np.arange(0, 10)
    y = np.arange(0, 10)
    scat = ax.scatter(x, y, s=15)
    PickCursor(scat)

func() ## this line does not work!!!

Anyone please shred some light on this? Thanks!


Solution

  • Your class is being garbage collected because you don't retain a reference to it.

    def func():
        # ...
        return PickCursor(scat)
    
    pc = func()