Matplotlib arrastra los puntos superpuestos interactivamente

En mi caso, solo quiero arrastrar un punto cada vez. Sin embargo, como los dos puntos se superponen en gran medida, arrastrar un punto provocará que se arrastre otro punto. ¿Cómo puedo solo arrastrar el punto que está arriba? ¡Gracias!

from pylab import * from scipy import * import matplotlib.pyplot as plt import matplotlib.patches as patches class DraggablePoint: def __init__(self, p): self.point = p self.press = None def connect(self): self.cidpress = self.point.figure.canvas.mpl_connect('button_press_event', self.button_press_event) self.cidrelease = self.point.figure.canvas.mpl_connect('button_release_event', self.button_release_event) self.cidmotion = self.point.figure.canvas.mpl_connect('motion_notify_event', self.motion_notify_event) def disconnect(self): 'disconnect all the stored connection ids' self.point.figure.canvas.mpl_disconnect(self.cidpress) self.point.figure.canvas.mpl_disconnect(self.cidrelease) self.point.figure.canvas.mpl_disconnect(self.cidmotion) def button_press_event(self,event): if event.inaxes != self.point.axes: return contains = self.point.contains(event)[0] if not contains: return self.press = self.point.center, event.xdata, event.ydata def button_release_event(self,event): self.press = None self.point.figure.canvas.draw() def motion_notify_event(self, event): if self.press is None: return if event.inaxes != self.point.axes: return self.point.center, xpress, ypress = self.press dx = event.xdata - xpress dy = event.ydata - ypress self.point.center = (self.point.center[0]+dx, self.point.center[1]+dy) print self.point.center self.point.figure.canvas.draw() if __name__ == '__main__': fig = plt.figure() ax = fig.add_subplot(111) ax.set_xlim(-1,2) ax.set_ylim(-1,2) circles = [] circle1 = patches.Circle((0.32,0.3), 0.2, fc='r', alpha=0.5, picker=True) circle = patches.Circle((0.3,0.3), 0.2, fc='b', alpha=0.5, picker=True) circles.append(ax.add_patch(circle1)) circles.append(ax.add_patch(circle)) drs = [] for c in circles: #print c.center[0] dr = DraggablePoint(c) dr.connect() drs.append(dr) plt.show() 

El método de Joe funciona bien, pero crea un conjunto de puntos de arrastre como una clase en lugar de una sola clase de punto de arrastre. Acabo de encontrar un método alternativo para resolver el problema anterior utilizando técnicas de animación de blit . No solo hace que el arrastre sea más rápido y suave, sino que también solo se puede arrastrar un punto. Vea el siguiente código.

 import matplotlib.pyplot as plt import matplotlib.patches as patches class DraggablePoint: lock = None #only one can be animated at a time def __init__(self, point): self.point = point self.press = None self.background = None def connect(self): 'connect to all the events we need' self.cidpress = self.point.figure.canvas.mpl_connect('button_press_event', self.on_press) self.cidrelease = self.point.figure.canvas.mpl_connect('button_release_event', self.on_release) self.cidmotion = self.point.figure.canvas.mpl_connect('motion_notify_event', self.on_motion) def on_press(self, event): if event.inaxes != self.point.axes: return if DraggablePoint.lock is not None: return contains, attrd = self.point.contains(event) if not contains: return self.press = (self.point.center), event.xdata, event.ydata DraggablePoint.lock = self # draw everything but the selected rectangle and store the pixel buffer canvas = self.point.figure.canvas axes = self.point.axes self.point.set_animated(True) canvas.draw() self.background = canvas.copy_from_bbox(self.point.axes.bbox) # now redraw just the rectangle axes.draw_artist(self.point) # and blit just the redrawn area canvas.blit(axes.bbox) def on_motion(self, event): if DraggablePoint.lock is not self: return if event.inaxes != self.point.axes: return self.point.center, xpress, ypress = self.press dx = event.xdata - xpress dy = event.ydata - ypress self.point.center = (self.point.center[0]+dx, self.point.center[1]+dy) canvas = self.point.figure.canvas axes = self.point.axes # restre the background region canvas.restre_region(self.background) # redraw just the current rectangle axes.draw_artist(self.point) # blit just the redrawn area canvas.blit(axes.bbox) def on_release(self, event): 'on release we reset the press data' if DraggablePoint.lock is not self: return self.press = None DraggablePoint.lock = None # turn off the rect animation property and reset the background self.point.set_animated(False) self.background = None # redraw the full figure self.point.figure.canvas.draw() def disconnect(self): 'disconnect all the stored connection ids' self.point.figure.canvas.mpl_disconnect(self.cidpress) self.point.figure.canvas.mpl_disconnect(self.cidrelease) self.point.figure.canvas.mpl_disconnect(self.cidmotion) fig = plt.figure() ax = fig.add_subplot(111) drs = [] circles = [patches.Circle((0.32, 0.3), 0.03, fc='r', alpha=0.5), patches.Circle((0.3,0.3), 0.03, fc='g', alpha=0.5)] for circ in circles: ax.add_patch(circ) dr = DraggablePoint(circ) dr.connect() drs.append(dr) plt.show() 

Sincronizaría qué artista está activo actualmente a través de una sola clase que trabaja con varios artistas.

Es más fácil usar el pick_event para esto. También facilita la generalización a otros artistas. Como ejemplo:

 import matplotlib.pyplot as plt import matplotlib.patches as patches class DraggablePoints(object): def __init__(self, artists, tolerance=5): for artist in artists: artist.set_picker(tolerance) self.artists = artists self.currently_dragging = False self.current_artist = None self.offset = (0, 0) for canvas in set(artist.figure.canvas for artist in self.artists): canvas.mpl_connect('button_press_event', self.on_press) canvas.mpl_connect('button_release_event', self.on_release) canvas.mpl_connect('pick_event', self.on_pick) canvas.mpl_connect('motion_notify_event', self.on_motion) def on_press(self, event): self.currently_dragging = True def on_release(self, event): self.currently_dragging = False self.current_artist = None def on_pick(self, event): if self.current_artist is None: self.current_artist = event.artist x0, y0 = event.artist.center x1, y1 = event.mouseevent.xdata, event.mouseevent.ydata self.offset = (x0 - x1), (y0 - y1) def on_motion(self, event): if not self.currently_dragging: return if self.current_artist is None: return dx, dy = self.offset self.current_artist.center = event.xdata + dx, event.ydata + dy self.current_artist.figure.canvas.draw() if __name__ == '__main__': fig, ax = plt.subplots() ax.set(xlim=[-1, 2], ylim=[-1, 2]) circles = [patches.Circle((0.32, 0.3), 0.2, fc='r', alpha=0.5), patches.Circle((0.3, 0.3), 0.2, fc='b', alpha=0.5)] for circ in circles: ax.add_patch(circ) dr = DraggablePoints(circles) plt.show()