Matplotlib以交互方式拖动重叠点

时间:2014-02-09 01:20:50

标签: python 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()

2 个答案:

答案 0 :(得分:13)

Joe的方法工作正常,但它将一组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()

答案 1 :(得分: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()