Draggable Matplotlib Subplot using wxPython
Question: I'm attempting to expand on a draggable plot tutorial by creating a subplot
that can be dragged (the matplotlib curve, not the whole window). I feel like
I'm close but just missing a critical detail.

Most of the code is just creating cookie cutter subplots, figure 3 is the only
one where I'm trying to drag the plot data.

Any help would be appreciated!

    
    
    import wxversion
    wxversion.ensureMinimal('2.8')
    
    import numpy as np
    
    import matplotlib
    matplotlib.interactive(True)
    
    matplotlib.use('WXAgg')
    from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
    from matplotlib.figure import Figure
    
    import wx
    
    class DraggableCurve:
    
        def __init__(self,curve):
            self.curve = curve[0]
            self.press = None
    
        def connect(self):
            'connect to all the events we need'
            self.cidpress = self.curve.figure.canvas.mpl_connect(
                'button_press_event', self.on_press)
            self.cidrelease = self.curve.figure.canvas.mpl_connect(
                'button_release_event', self.on_release)
            self.cidmotion = self.curve.figure.canvas.mpl_connect(
                'motion_notify_event', self.on_motion)
    
        def on_press(self, event):
            print "on_press"
            'on button press we will see if the mouse is over us and store some data'
            if event.inaxes != self.curve.axes: return
    
            contains, attrd = self.curve.contains(event)
            if not contains: return
            print 'event contains', self.curve.xy
            x0, y0 = self.curve.xy
            # print x0,y0
            self.press = x0, y0, event.xdata, event.ydata
    
        def on_motion(self, event):
            print "on_motion"
            'on motion we will move the curve if the mouse is over us'
            if self.press is None: return
            if event.inaxes != self.curve.axes: return
            x0, y0, xpress, ypress = self.press
            print xpress, ypress
            dx = event.xdata - xpress
            dy = event.ydata - ypress
            #print 'x0=%f, xpress=%f, event.xdata=%f, dx=%f, x0+dx=%f'%(x0, xpress, event.xdata, dx, x0+dx)
            self.curve.set_x(x0+dx)
            self.curve.set_y(y0+dy)
            # print x0+dx, y0+dy
    
            #self.curve.figure.canvas.draw()
            self.curve.figure.canvas.draw_idle()
    
        def on_release(self, event):
            print "on_release"
            'on release we reset the press data'
            self.press = None
            #self.curve.figure.canvas.draw()
            self.curve.figure.canvas.draw_idle()
    
        def disconnect(self):
            'disconnect all the stored connection ids'
            self.curve.figure.canvas.mpl_disconnect(self.cidpress)
            self.curve.figure.canvas.mpl_disconnect(self.cidrelease)
            self.curve.figure.canvas.mpl_disconnect(self.cidmotion)
    
    class CanvasFrame(wx.Frame):
    
        def __init__(self):
    
            #create frame
            frame = wx.Frame.__init__(self,None,-1,
                             'Test',size=(550,350))
    
            #set background
            self.SetBackgroundColour(wx.NamedColour("WHITE"))
    
            #initialize figures
            self.figure1 = Figure()
            self.figure2 = Figure()
            self.figure3 = Figure()
            self.figure4 = Figure()
    
            #initialize figure1
            self.axes1 = self.figure1.add_subplot(111)
            self.axes1.text(0.5,0.5, 'Test 1', horizontalalignment='center', fontsize=15)
            self.axes1.get_xaxis().set_visible(False)
            self.axes1.get_yaxis().set_visible(False)
            self.canvas1 = FigureCanvas(self, -1, self.figure1)
    
            #initialize figure2
            self.axes2 = self.figure2.add_subplot(111)
            self.axes2.text(0.5,0.5, 'Test 2', horizontalalignment='center', fontsize=15)
            self.axes2.get_xaxis().set_visible(False)
            self.axes2.get_yaxis().set_visible(False)
            self.canvas2 = FigureCanvas(self, -1, self.figure2)
    
            #initialize figure3
            self.axes3 = self.figure3.add_subplot(111)
            curve = self.axes3.plot(np.arange(1,11),10*np.random.rand(10),color='r',marker='o')
            self.canvas3 = FigureCanvas(self, -1, self.figure3)
            # self.axes3.get_xaxis().set_visible(True)
            # self.axes3.get_yaxis().set_visible(True)
            # self.canvas3.draw()
            # self.canvas3.draw_idle()
            dc = DraggableCurve(curve)
            dc.connect()
    
            #initialize figure4
            self.axes4 = self.figure4.add_subplot(111)
            self.axes4.text(0.5,0.5, 'Test4', horizontalalignment='center', fontsize=15)
            self.axes4.get_xaxis().set_visible(False)
            self.axes4.get_yaxis().set_visible(False)
            self.canvas4 = FigureCanvas(self, -1, self.figure4)
    
            #create figures into the 2x2 grid
            self.sizer = wx.GridSizer(rows=2, cols=2, hgap=5, vgap=5)
            self.sizer.Add(self.canvas1, 1, wx.EXPAND)
            self.sizer.Add(self.canvas2, 1, wx.EXPAND)
            self.sizer.Add(self.canvas3, 1, wx.EXPAND)
            self.sizer.Add(self.canvas4, 1, wx.EXPAND)
            self.SetSizer(self.sizer)
            self.Fit()
    
            return
    
    class App(wx.App):
    
        def OnInit(self):
            'Create the main window and insert the custom frame'
            frame = CanvasFrame()
            frame.Show(True)
    
            return True
    
    app = App(0)
    app.MainLoop()
    

[![enter image description
here](http://i.stack.imgur.com/DYTJD.png)](http://i.stack.imgur.com/DYTJD.png)


Answer: Check this example:

    
    
    # -*- coding: utf-8 -*-
    import wxversion
    wxversion.ensureMinimal('2.8')
    import wx
    import numpy as np
    import matplotlib
    matplotlib.use('WXAgg')
    from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg
    from matplotlib.figure import Figure
    
    class FigureCanvas(FigureCanvasWxAgg):
        def __init__(self,parent,id,figure,**kwargs):
            FigureCanvasWxAgg.__init__(self,parent=parent, id=id, figure=figure,**kwargs)
            self.figure = figure
            self.axes = self.figure.get_axes()[0] # Get axes
            self.connect() # Connect event
    
        def connect(self):
            """Connect pick event"""
            self.MOVE_LINE_EVT = self.mpl_connect("pick_event", self.on_pick)
    
        def on_pick(self,event):
            self._selected_line = event.artist # Get selected line
            # Get initial x,y data
            self._p0 = (event.mouseevent.xdata, event.mouseevent.ydata)
            self._xdata0 = self._selected_line.get_xdata()
            self._ydata0 = self._selected_line.get_ydata()
            # Connect events for motion and release.
            self._on_motion = self.mpl_connect("motion_notify_event", self.on_motion)
            self._on_release = self.mpl_connect("button_release_event", self.on_release)
    
        def on_motion(self,event):
            cx = event.xdata # Current xdata
            cy = event.ydata # Current ydata
            deltax = cx - self._p0[0]
            deltay = cy - self._p0[1]
            self._selected_line.set_xdata(self._xdata0 + deltax)
            self._selected_line.set_ydata(self._ydata0 + deltay)
            self.draw()
    
        def on_release(self,event):
            """On release, disconnect motion and release"""
            self.mpl_disconnect(self._on_motion)
            self.mpl_disconnect(self._on_release)
            self.axes.relim()
            self.axes.autoscale_view(True,True,True)
            self.draw()
    
    
    class Frame(wx.Frame):
        def __init__(self,parent,title):
            wx.Frame.__init__(self,parent,title=title,size=(800,600))
            self.initCtrls()
            self.plotting()
            self.Centre(True)
            self.Show()
    
        def initCtrls(self):
            self.mainsizer = wx.GridSizer(rows=2, cols=2, hgap=2, vgap=2)
            # 1
            self.figure = Figure()
            self.axes = self.figure.add_subplot(111)
            self.canvas = FigureCanvas(self, wx.ID_ANY, self.figure)
    
            # 2
            self.figure2 = Figure()
            self.axes2 = self.figure2.add_subplot(111)
            self.canvas2 = FigureCanvas(self, wx.ID_ANY, self.figure2)
    
            self.figure3 = Figure()
            self.axes3 = self.figure3.add_subplot(111)
            self.canvas3 = FigureCanvas(self, wx.ID_ANY, self.figure3)
    
            self.figure4 = Figure()
            self.axes4 = self.figure4.add_subplot(111)
            self.canvas4 = FigureCanvas(self, wx.ID_ANY, self.figure4)
    
            self.mainsizer.Add(self.canvas, 1, wx.EXPAND)
            self.mainsizer.Add(self.canvas2, 1, wx.EXPAND)
            self.mainsizer.Add(self.canvas3, 1, wx.EXPAND)
            self.mainsizer.Add(self.canvas4, 1, wx.EXPAND)
            self.SetSizer(self.mainsizer)
    
        def plotting(self):
            # Set picker property -> true
            self.axes2.plot(np.arange(1,11),10*np.random.rand(10),color='b',
                           marker='o', picker=True)
            self.axes3.plot(np.arange(1,11),10*np.random.rand(10),color='r',
                           marker='o', picker=True)
            self.canvas.draw()
    
    
    if __name__=='__main__':
        app = wx.App()
        frame = Frame(None, "Matplotlib Demo")
        frame.Show()
        app.MainLoop()
    

Basically the idea is to define a custom FigureCanvas, which supports the
selection and movement of the lines using the pick event.

Obviously this code still needs a lot of review, to work properly.

