Matplotlib drag overlapping points interactively

Hi Matplotlib community:

In my case, I only want to drag one point each time. However, since the two points are heavily overlapping, dragging one point would cause another point to be dragged. How can I only drag one of the point? Thank you!

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

Hi Yi,

I also posted this as an answer to your question on SO, but here’s how I’d approach it.

It’s simplest to just have a “DraggablePoints” class that accepts multiple artists. That way you can synchronize which artist is “active” and ignore the others.

As an example, based on yours:

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


Hope that helps!
-Joe

<details class='elided'>
<summary title='Show trimmed content'>&#183;&#183;&#183;</summary>

On Sat, Feb 8, 2014 at 7:32 PM, Yi <idealistyy@...287...> wrote:
> Hi Matplotlib community:
> 

> In my case, I only want to drag one point each time. However, since the two points are heavily overlapping, dragging one point would cause another point to be dragged. How can I only drag one of the point? Thank you!

> 

> ```
> 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()
> ```

> 

> 
> ------------------------------------------------------------------------------
> 
> Managing the Performance of Cloud-Based Applications
> 
> Take advantage of what the Cloud has to offer - Avoid Common Pitfalls.
> 
> Read the Whitepaper.
> 
> [http://pubads.g.doubleclick.net/gampad/clk?id=121051231&iu=/4140/ostg.clktrk](http://pubads.g.doubleclick.net/gampad/clk?id=121051231&iu=/4140/ostg.clktrk)
> _______________________________________________
> 
> 
> Matplotlib-users mailing list
> 
> Matplotlib-users@lists.sourceforge.net
> 
> [https://lists.sourceforge.net/lists/listinfo/matplotlib-users](https://lists.sourceforge.net/lists/listinfo/matplotlib-users)
> 
>

</details>