2014-02-09 3 views
5

В моем случае я хочу только перетаскивать одну точку каждый раз. Однако, поскольку две точки сильно перекрываются, перетаскивание одной точки приведет к перетаскиванию другой точки. Как я могу перетащить только ту точку, которая указана выше? Спасибо!Matplotlib перетаскивание точек перекрытия в интерактивном режиме

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() 
+0

как вы узнаете, в какой точке находится вышеуказанное? – zhangxaochen

+0

Вы правы, или, может быть, вопрос должен быть: как я могу только перетащить одну из точек? вместо двух точек, движущихся вместе. – Idealist

+0

Превосходно сформулированный вопрос с очень хорошим примером, кстати! –

ответ

9

Джо работает отлично, но это делает набор draggablepoints как класс вместо одного класса draggablepoint. Я только что наткнулся на альтернативный метод решения вышеуказанной проблемы, используя animation blit techniques. Это не только делает перетаскивание более быстрым и плавным, но также можно только перетаскивать только одну точку. См. Следующий код.

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 
     # restore the background region 
     canvas.restore_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() 
6

Я бы синхронизировал, какой художник в настоящее время активен через один класс, который работает с несколькими художниками.

Для этого проще всего использовать pick_event. Это также облегчает обобщение для других художников. В качестве примера: метод

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() 
Смежные вопросы