0

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

0

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Awesome! This will help a ton. Thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.