2

I believe I am not using Matplotlib's pick events properly. In the following code, I create three disjoint orange disks of specified radius and position identified by an id number.

I want to single-click on each of the disks and print out a message to the terminal identifying the disks, center, radius and id. However, everytime I click on a disk, the pick-events for all the disks are fired. Where am I going wrong?

Here is the plot

enter image description here

Here is the output on clicking disk 1

enter image description here

Here is the code.

from global_config import GC
import matplotlib as mpl
import matplotlib.pyplot as plt 
import numpy as np


class Disk:


    def __init__(self, center, radius, myid = None, figure=None, axes_object=None):
        """ 
        @ARGS
        CENTER : Tuple of floats
        RADIUS : Float
        """
        self.center = center
        self.radius = radius
        self.fig    = figure
        self.ax     = axes_object
        self.myid   = myid

    def onpick(self,event):
        print "You picked the disk ", self.myid, "  with Center: ", self.center, " and Radius:", self.radius


    def mpl_patch(self, diskcolor= 'orange' ):
        """ Return a Matplotlib patch of the object
        """
        mypatch =  mpl.patches.Circle( self.center, self.radius, facecolor = diskcolor, picker=1 )

        if self.fig != None:
            self.fig.canvas.mpl_connect('pick_event', self.onpick) # Activate the object's method

        return mypatch



def main():

    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.set_title('click on disks to print out a message')

    disk_list = []

    disk_list.append( Disk( (0,0), 1.0, 1, fig, ax   )   ) 
    ax.add_patch(disk_list[-1].mpl_patch() )

    disk_list.append( Disk( (3,3), 0.5, 2, fig, ax   )   )
    ax.add_patch(disk_list[-1].mpl_patch() )

    disk_list.append( Disk( (4,9), 2.5, 3, fig, ax   )   )
    ax.add_patch(disk_list[-1].mpl_patch() )


    ax.set_ylim(-2, 10);
    ax.set_xlim(-2, 10);

    plt.show()


if __name__ == "__main__":
    main()

1 Answer 1

6

There are a number of ways to handle this. I've modified your code to try to show two possible ways (so it has extraneous code though matter which way you'd like to handle it).

Generally, I think you'd only attach a single pick_event handler, and that handler would need to determine which object was hit. The code below works that way with the on_pick function capturing your disks, and patches, and then returning a function to tell which figure was clicked.

If you wanted to stick with attaching multiple pick_event handlers, you could do that by tweaking the Disk.onpick as I have below to determine if the pick event was relevant to this disk (each disk will get every pick event). You'll note that the Disk class saves off its patch in self.mypatch for this to work. If you wanted to do it this way, discard all the changes I've made to main, and uncomment the two lines in Disk.mpl_patch.

import matplotlib as mpl
import matplotlib.pyplot as plt 
import numpy as np


class Disk:


    def __init__(self, center, radius, myid = None, figure=None, axes_object=None):
        """ 
        @ARGS
        CENTER : Tuple of floats
        RADIUS : Float
        """
        self.center = center
        self.radius = radius
        self.fig    = figure
        self.ax     = axes_object
        self.myid   = myid
        self.mypatch = None


    def onpick(self,event):
        if event.artist == self.mypatch:
            print "You picked the disk ", self.myid, "  with Center: ", self.center, " and Radius:", self.radius


    def mpl_patch(self, diskcolor= 'orange' ):
        """ Return a Matplotlib patch of the object
        """
        self.mypatch = mpl.patches.Circle( self.center, self.radius, facecolor = diskcolor, picker=1 )

        #if self.fig != None:
            #self.fig.canvas.mpl_connect('pick_event', self.onpick) # Activate the object's method

        return self.mypatch

def on_pick(disks, patches):
    def pick_event(event):
        for i, artist in enumerate(patches):
            if event.artist == artist:
                disk = disks[i]
                print "You picked the disk ", disk.myid, "  with Center: ", disk.center, " and Radius:", disk.radius
    return pick_event


def main():

    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.set_title('click on disks to print out a message')

    disk_list = []
    patches = []

    disk_list.append( Disk( (0,0), 1.0, 1, fig, ax   )   ) 
    patches.append(disk_list[-1].mpl_patch())
    ax.add_patch(patches[-1])

    disk_list.append( Disk( (3,3), 0.5, 2, fig, ax   )   )
    patches.append(disk_list[-1].mpl_patch())
    ax.add_patch(patches[-1])

    disk_list.append( Disk( (4,9), 2.5, 3, fig, ax   )   )
    patches.append(disk_list[-1].mpl_patch()) 
    ax.add_patch(patches[-1])

    pick_handler = on_pick(disk_list, patches)

    fig.canvas.mpl_connect('pick_event', pick_handler) # Activate the object's method

    ax.set_ylim(-2, 10);
    ax.set_xlim(-2, 10);

    plt.show()


if __name__ == "__main__":
    main()
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you so much! That worked perfectly! And i got to learn more about Python and Matplotlib in the process. Much appreciated.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.