Reputation: 89
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
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