user2920100
user2920100

Reputation: 89

Python GUI that draw a dot when clicking on plot?

First try at interactive plotting, so any help is welcome.

I'm trying to get an interactive matplotlib plot where dots are plotted every time the mouse is clicked. The dots are to be plotted at the location of the click and on top of an image. I think I have figure out how to do that, but I'm wondering if there's an easy way to add an "undo" button so that I can remove the last plotted dot if needed. In the same line of idea, I would also like to add "reset" (i.e. remove all dots) and "save" buttons.

from matplotlib import pyplot as plt

def onclick(event):
    button=event.button
    x=event.xdata
    y=event.ydata

    if button==1: plt.plot(x,y,'ro')
    if button!=1: plt.plot(x,y,'bo')
    print 'button=%d, x=%d, y=%d, xdata=%f, ydata=%f'%(
    event.button, event.x, event.y, event.xdata, event.ydata)

im = plt.imread('Picture1.png')
fig, ax=plt.subplots()
ax.imshow(im)
ax.autoscale(False)
cid = fig.canvas.mpl_connect('button_press_event', onclick)

plt.show()

Upvotes: 4

Views: 3363

Answers (1)

tacaswell
tacaswell

Reputation: 87376

You just need to write the logic to do it. This is a slightly modified version of something I use (and only maintains one list) but it should be easy to extend this to do what you want.

class clicker_class(object):
    def __init__(self, ax, pix_err=1):
        self.canvas = ax.get_figure().canvas
        self.cid = None
        self.pt_lst = []
        self.pt_plot = ax.plot([], [], marker='o',
                               linestyle='none', zorder=5)[0]
        self.pix_err = pix_err
        self.connect_sf()

    def set_visible(self, visible):
        '''sets if the curves are visible '''
        self.pt_plot.set_visible(visible)

    def clear(self):
        '''Clears the points'''
        self.pt_lst = []
        self.redraw()

    def connect_sf(self):
        if self.cid is None:
            self.cid = self.canvas.mpl_connect('button_press_event',
                                               self.click_event)

    def disconnect_sf(self):
        if self.cid is not None:
            self.canvas.mpl_disconnect(self.cid)
            self.cid = None

    def click_event(self, event):
        ''' Extracts locations from the user'''
        if event.key == 'shift':
            self.pt_lst = []
            return
        if event.xdata is None or event.ydata is None:
            return
        if event.button == 1:
            self.pt_lst.append((event.xdata, event.ydata))
        elif event.button == 3:
            self.remove_pt((event.xdata, event.ydata))

        self.redraw()

    def remove_pt(self, loc):
        if len(self.pt_lst) > 0:
            self.pt_lst.pop(np.argmin(map(lambda x:
                                          np.sqrt((x[0] - loc[0]) ** 2 +
                                                  (x[1] - loc[1]) ** 2),
                                          self.pt_lst)))

    def redraw(self):
        if len(self.pt_lst) > 0:
            x, y = zip(*self.pt_lst)
        else:
            x, y = [], []
        self.pt_plot.set_xdata(x)
        self.pt_plot.set_ydata(y)

        self.canvas.draw()

    def return_points(self):
        '''Returns the clicked points in the format the rest of the
        code expects'''
        return np.vstack(self.pt_lst).T

ax = gca()
cc = clicker_class(ax)

Upvotes: 4

Related Questions