Search code examples
wxpythonpython-3.6matplotlib-widget

how make 2 rectangle selector move in the same times and in the same area


i need to define 2 rectangleselector that moves at the same time and in same region, when i move one another moves at the same time how i can do that ?

that is a simple code with 2 plot in 2 panel and 2 rectangle

is possible to make the two rectangle move in the same time cause i didn't find any exemple for that in the web or in matplotlib

i use wxpython 4 for python3.6

import wx
from numpy import arange, sin, pi,cos
import numpy as np
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.widgets import RectangleSelector
from matplotlib.figure import Figure



class MainFrame(wx.Frame):
    def __init__(self, parent ):
        wx.Panel.__init__(self, parent,name="Main", size = (600,800))
        Top = PanelTop(self)
        Bottom = PanelBottom(self)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(Top, 1, wx.EXPAND)
        sizer.Add(Bottom, 1, wx.EXPAND)
        self.SetSizer(sizer)


class PanelTop(wx.Panel):
    def __init__(self,parent):
        wx.Panel.__init__(self,parent,size = (300,300))
        self.SetBackgroundColour('white')
        self.figure = Figure(figsize = (4,5))
        self.axes = self.figure.add_subplot(111)
        self.canvas = FigureCanvas(self,-1,self.figure)
        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
        self.SetSizer(self.sizer)


        t = arange(0.5, 3.0, 0.01)
        s = cos(2 * pi * t)
        self.axes.plot(t, s)
        self.RS = RectangleSelector(self.axes,self.line_select_callback,
                                       drawtype='box', useblit=False,
                                       button=[1, 3],minspanx=1, minspany=1,
                                       spancoords='pixels',
                                       interactive=True, rectprops = dict(facecolor='None',edgecolor='red',alpha=5,fill=False))
        self.RS.to_draw.set_visible(True)
        self.RS.extents = (1,0,0,1)



    def line_select_callback(self, eclick, erelease):
         x1, y1 = eclick.xdata, eclick.ydata
         x2, y2 = erelease.xdata, erelease.ydata


class PanelBottom(wx.Panel):
    def __init__(self,parent):
        wx.Panel.__init__(self, parent, size = (300,300))
        self.SetBackgroundColour('grey77')
        self.figure = Figure(figsize = (4,5))
        self.axes = self.figure.add_subplot(111)
        self.canvas = FigureCanvas(self,-1,self.figure)
        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
        self.SetSizer(self.sizer)

        t = arange(0.0, 3.0, 0.01)
        s = sin(2 * pi * t)
        self.axes.plot(t, s)
        self.RS = RectangleSelector(self.axes,self.line_select_callback,
                                       drawtype='box', useblit=False,
                                       button=[1, 3],minspanx=1, minspany=1,
                                       spancoords='pixels',
                                       interactive=True, rectprops = dict(facecolor='None',edgecolor='red',alpha=5,fill=False))
        self.RS.to_draw.set_visible(True)
        self.RS.extents = (1,0,0,1)



    def line_select_callback(self, eclick, erelease):
         x1, y1 = eclick.xdata, eclick.ydata
         x2, y2 = erelease.xdata, erelease.ydata



app = wx.App()
frame = MainFrame(None).Show()
app.MainLoop()

thank you in advance


Solution

  • I have no idea if this is the way that it should be done but here is one way.
    I've commented the code where appropriate.
    The method simply grubs the cordinates of the first Rectangle forces them into the second and then redraws it.
    The second rectangle is made Inactive as it is meant to be a slave of the first and if it isn't inactive, the control points at the corners and middle, fail to move with the rectangle. This leads me to believe that there may be a less hacky way of achieving the same result.

    import wx
    from numpy import arange, sin, pi,cos
    import numpy as np
    from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
    from matplotlib.widgets import RectangleSelector
    from matplotlib.figure import Figure
    
    class MainFrame(wx.Frame):
        def __init__(self, parent ):
            wx.Panel.__init__(self, parent,name="Main", size = (600,800))
            self.Top = PanelTop(self)
            self.Bottom = PanelBottom(self)
            sizer = wx.BoxSizer(wx.VERTICAL)
            sizer.Add(self.Top, 1, wx.EXPAND)
            sizer.Add(self.Bottom, 1, wx.EXPAND)
            self.SetSizer(sizer)
    
    class PanelTop(wx.Panel):
        def __init__(self,parent):
            wx.Panel.__init__(self,parent,size = (300,300))
            self.SetBackgroundColour('white')
            self.figure = Figure(figsize = (4,5))
            self.axes = self.figure.add_subplot(111)
            self.canvas = FigureCanvas(self,-1,self.figure)
            self.sizer = wx.BoxSizer(wx.VERTICAL)
            self.sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
            self.SetSizer(self.sizer)
    
    
            t = arange(0.5, 3.0, 0.01)
            s = cos(2 * pi * t)
            self.axes.plot(t, s)
            self.RS = RectangleSelector(self.axes,self.line_select_callback,
                                           drawtype='box', useblit=False,
                                           button=[1, 3],minspanx=1, minspany=1,
                                           spancoords='pixels',
                                           interactive=True, rectprops = dict(facecolor='None',edgecolor='red',alpha=5,fill=False))
            self.RS.to_draw.set_visible(True)
            self.RS.extents = (1,0,0,1)
    
        def line_select_callback(self, eclick, erelease):
            x1, y1 = eclick.xdata, eclick.ydata
            x2, y2 = erelease.xdata, erelease.ydata
            rect = self.RS.artists[0] # Rectangle artist
            slave_rect = self.Parent.Bottom.RS.artists[0] # Rectangle artist
            slave_canvas = self.Parent.Bottom.canvas
    
            # Force new positional values into slave rectangle
            slave_rect.set_x(x1)
            slave_rect.set_y(y1)
            slave_rect.set_width(x2-x1)
            slave_rect.set_height(y2-y1)
            slave_rect.update(dict(facecolor='None',edgecolor='red',alpha=5,fill=False))
            #Force visible in case slave rectangle has been clicked on
            slave_rect.set_visible(True)
            #Redraw slave rectangle
            slave_canvas.draw()
    
    class PanelBottom(wx.Panel):
        def __init__(self,parent):
            wx.Panel.__init__(self, parent, size = (300,300))
            self.SetBackgroundColour('grey77')
            self.figure = Figure(figsize = (4,5))
            self.axes = self.figure.add_subplot(111)
            self.canvas = FigureCanvas(self,-1,self.figure)
            self.sizer = wx.BoxSizer(wx.VERTICAL)
            self.sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
            self.SetSizer(self.sizer)
    
            t = arange(0.0, 3.0, 0.01)
            s = sin(2 * pi * t)
            self.axes.plot(t, s)
            self.RS = RectangleSelector(self.axes,self.line_select_callback,
                                           drawtype='box', useblit=False,
                                           button=[1, 3],minspanx=1, minspany=1,
                                           spancoords='pixels',
                                           interactive=False, rectprops = dict(facecolor='None',edgecolor='red',alpha=5,fill=False))
            self.RS.to_draw.set_visible(True)
            self.RS.extents = (1,0,0,1)
    
        def line_select_callback(self, eclick, erelease):
            x1, y1 = eclick.xdata, eclick.ydata
            x2, y2 = erelease.xdata, erelease.ydata
    
    app = wx.App()
    frame = MainFrame(None).Show()
    app.MainLoop()