Exemple #1
0
class click_window:
    '''An interactive window.  Given an axis instance and a start point
   (x0,y0), draw a dynamic rectangle that follows the mouse until
   the close() function is called (which returns the coordinates of
   the final rectangle.  Useful or selecting out square regions.'''
    def __init__(self, ax, x0, y0):
        self.ax = ax
        self.x0 = x0
        self.y0 = y0
        self.rect = Rectangle((x0, y0), width=0, height=0, alpha=0.1)
        ax.add_artist(self.rect)

    def connect(self):
        self.cidmotion = self.rect.figure.canvas.mpl_connect(
            'motion_notify_event', self.on_motion)

    def on_motion(self, event):
        # Have we left the axes?
        if event.inaxes != self.rect.axes: return

        self.rect.set_width(event.xdata - self.x0)
        self.rect.set_height(event.ydata - self.y0)
        self.ax.figure.canvas.draw()

    def close(self):
        self.rect.figure.canvas.mpl_disconnect(self.cidmotion)
        extent = self.rect.get_bbox().get_points()
        self.rect.remove()
        self.ax.figure.canvas.draw()
        return (list(ravel(extent)))
Exemple #2
0
class click_window:
   '''An interactive window.  Given an axis instance and a start point
   (x0,y0), draw a dynamic rectangle that follows the mouse until
   the close() function is called (which returns the coordinates of
   the final rectangle.  Useful or selecting out square regions.'''

   def __init__(self, ax, x0, y0):
      self.ax = ax
      self.x0 = x0
      self.y0 = y0
      self.rect = Rectangle((x0,y0), width=0, height=0, alpha=0.1)
      ax.add_artist(self.rect)

   def connect(self):
      self.cidmotion = self.rect.figure.canvas.mpl_connect(
            'motion_notify_event', self.on_motion)

   def on_motion(self, event):
      # Have we left the axes?
      if event.inaxes != self.rect.axes:  return

      self.rect.set_width(event.xdata - self.x0)
      self.rect.set_height(event.ydata - self.y0)
      self.ax.figure.canvas.draw()

   def close(self):
      self.rect.figure.canvas.mpl_disconnect(self.cidmotion)
      extent = self.rect.get_bbox().get_points()
      self.rect.remove()
      self.ax.figure.canvas.draw()
      return(list(ravel(extent)))
Exemple #3
0
class Brusher():
    '''
    A generic brusher.
    
    Expects:
    fig:  a mpl figure with subplots
      NOTE: each individual point, in each subplot, must be assigned its own color.
            e.g. use `scatter(x_coords, y_coords, c=array_of_rgba_colors)`
    data: a tuple of numpy arrays, with one entry for each axis in fig, in order (i.e. left->right then top->bottom)
      each item in the tuple must by a 2d numpy array with:
      array[0] --> 1d array of x coordinates
      array[1] --> 1d array of y coordinates
      and each list must be in the same order.
      i.e. the nth pair of values in the first array of the tuple must correspond
           to the nth pair of values in every other array in the tuple.
    '''
    
    def __init__(self, fig, data):
        '''
        fig:  a pre-rendered figure, with subplot axes
        data: the data represented in fig, formatted as described above
        '''
        self.fig  = fig
        self.data = data
        self.axl  = fig.get_axes()
        
        # internal things to keep track of
        self.button_down = False
        self.brush       = None
        self.brush_axis  = None
        self.start_xy    = (None, None)
        
        # define the colors we want to use (overwrites original figure's colors)
        self.unbrushed_color = [.5, .5, .5, .5]  # this is a transparent gray
        self.brushed_color   = [0., 0., 1., .75]  # this is a transparent blue

        # register events to track
        self.fig.canvas.mpl_connect('button_press_event', self.button_press)
        self.fig.canvas.mpl_connect('motion_notify_event', self.mouse_motion)
        self.fig.canvas.mpl_connect('button_release_event', self.button_release)


    def button_press(self, event):
        ''' handles a mouse click '''
        # if clicked outside of axes, ignore
        if not event.inaxes: return

        # remove old brush object if it's there
        if self.brush:
            self.brush.remove()
            self.unbrush_me()
            plt.draw()
    
        # start a brush here
        self.button_down = True
        self.start_xy    = (event.xdata,event.ydata)
        self.brush       = Rectangle( self.start_xy, 0,0, facecolor='gray', alpha=.15 )
        self.brush_axis  = event.inaxes
        
        # add brush to axis
        event.inaxes.add_patch(self.brush)
        plt.draw()
        
        
    def mouse_motion(self, event):
        ''' handles the mouse moving around '''
        # if we haven't already clicked and started a brush, ignore the motion
        if not (self.button_down and self.brush): return
        # if we've moved the mouse outside the starting axis, ignore
        if self.brush_axis != event.inaxes: return

        # otherwise, track the motion and update the brush
        self.brush.set_width( abs(self.start_xy[0] - event.xdata) )
        self.brush.set_height( abs(self.start_xy[1] - event.ydata) )
        self.brush.set_x( min(self.start_xy[0], event.xdata) )
        self.brush.set_y( min(self.start_xy[1], event.ydata) )
        plt.draw()


    def button_release(self, event):
        ''' handles a mouse button release '''
        self.button_down = False
        
        # if we didn't start with a brush, ignore
        if not self.brush: return
        
        # if we just clicked without drawing, remove the brush
        if (event.xdata, event.ydata) == self.start_xy:
            self.unbrush_me()
            plt.draw()
            return
        
        # if we stopped inside the same axis, update the brush one last time
        if event.inaxes == self.brush_axis:
            self.brush.set_width( abs(self.start_xy[0] - event.xdata) )
            self.brush.set_height( abs(self.start_xy[1] - event.ydata) )
            self.brush.set_x( min(self.start_xy[0], event.xdata) )
            self.brush.set_y( min(self.start_xy[1], event.ydata) )
        
        self.brush_me()
        plt.draw()
        
        
    def brush_me(self):
        ''' apply the new brush to data in all axes '''
        # figure out the indices of the data inside the brush
        i_brush_axis = self.axl.index(self.brush_axis)
        ax_data = self.data[i_brush_axis]
        bbox    = self.brush.get_bbox()
        boolean_brushed = []
        for i in range(len(ax_data[0])):
            if bbox.contains( ax_data[0,i], ax_data[1,i] ):
                boolean_brushed.append(True)
            else:
                boolean_brushed.append(False)
        boolean_brushed  = np.array(boolean_brushed)
        # boolean_brushed is an array of T,F values indicating whether the datapoint at
        #  that index should be 'brushed' (i.e. keep its color) or not (made gray)
        
        for i, ax in enumerate(self.axl):
            plotted_things = ax.collections[0]
            # now set the facecolor of all points not brushed to gray
            #  new_fc is a 2d array, the 0th axis corresponding to individual points,
            #  and the 1st axis corresponding to color values
            new_fc = plotted_things.get_facecolors()
            new_fc[~boolean_brushed] = self.unbrushed_color
            new_fc[boolean_brushed]  = self.brushed_color
    
    
    def unbrush_me(self):
        ''' paint everything the brushed color '''
        # build an always-true boolean array and brush to that
        boolean_brushed = []
        for i in range(len(self.data[0][0])):
            boolean_brushed.append(True)
        boolean_brushed = np.array(boolean_brushed)
        for i, ax in enumerate(self.axl):
            plotted_things = ax.collections[0]
            new_fc = plotted_things.get_facecolors()
            new_fc[boolean_brushed]  = self.brushed_color
Exemple #4
0
class RectangleCreationOnClick(PanOnClick):
    """Class providing rectangle creation patch to a matplotlib Figure.
    Left button to create a rectangle with the desired width.
    """
    def __init__(self, figure=None, scale_factor=1.1):
        """Initializer
        :param Figure figure: The matplotlib figure to attach the behavior to.
        :param float scale_factor: The scale factor to apply on wheel event.
        """
        super(RectangleCreationOnClick, self).__init__(figure,
                                                       scale_factor=1.1)
        self._add_connection_rectangle_creation('button_press_event',
                                                self._on_mouse_press_creation)
        self._add_connection_rectangle_creation(
            'button_release_event', self._on_mouse_release_creation)
        self._add_connection_rectangle_creation('motion_notify_event',
                                                self._on_mouse_motion_creation)

        self._pressed_button = None  # To store active button
        self._axes = None  # To store x and y axes concerned by interaction
        self._event = None  # To store reference event during interaction

    def update_current_rectangle(self, ax, curr_event, past_event):
        """Create the rectangle with the width stablished from initial click
        and current position of the mouse
        :param matplotlib.axes._subplots.AxesSubplot ax: axis where the rectangle is gonna be drawed or it's width modified
        :param matplotlib.backend_bases.MouseEvent curr_event: data from the current position of the mouse of the motion event
        :param matplotlib.backend_bases.MouseEvent past_event: data from the initial position where a click has been done
        """
        try:
            #If current click does not goes to the limit (which would cause to return None)
            if curr_event.xdata != None:

                yAxis_limits = self._rectangle_yAxis_limits
                #If rectangle creation goes from left to right
                if curr_event.xdata < past_event.xdata:

                    #If the rectangle has not been created, create it
                    if not isinstance(self._rectangle_interactions, Rectangle):

                        self._rectangle_interactions = Rectangle(
                            [curr_event.xdata, yAxis_limits[0] * 0.9],
                            abs(past_event.xdata - curr_event.xdata),
                            yAxis_limits[1] * 1.1,
                            color='black',
                            alpha=0.3)

                        self._rectangleStats.direction = -1
                        ax.add_patch(self._rectangle_interactions)
                    #If it has been created and goes from left to right, update width
                    elif isinstance(self._rectangle_interactions, Rectangle
                                    ) and self._rectangleStats.direction == -1:

                        self._rectangle_interactions.set_height(
                            abs(yAxis_limits[0] * 0.9 - yAxis_limits[1] * 1.1))
                        self._rectangle_interactions.set_y(yAxis_limits[0] *
                                                           0.9)
                        self._rectangle_interactions.set_x(curr_event.xdata)
                        self._rectangle_interactions.set_width(
                            abs(past_event.xdata - curr_event.xdata))

                    #If it has been created but changed direction from right-left to left-right, update width
                    elif self._rectangleStats.direction == 1:

                        self._rectangle_interactions.set_height(
                            abs(yAxis_limits[0] * 0.9 - yAxis_limits[1] * 1.1))
                        self._rectangle_interactions.set_y(yAxis_limits[0] *
                                                           0.9)
                        self._rectangleStats.direction = -1
                        self._rectangle_interactions.set_x(curr_event.xdata)
                        self._rectangle_interactions.set_width(
                            abs(past_event.xdata - curr_event.xdata))

                #If rectangle creation goes from right to left
                else:
                    #If the rectangle has not been created, create it
                    if not isinstance(self._rectangle_interactions, Rectangle):
                        self._rectangle_interactions = Rectangle(
                            [past_event.xdata, yAxis_limits[0] * 0.9],
                            abs(past_event.xdata - curr_event.xdata),
                            yAxis_limits[1] * 1.1,
                            color='black',
                            alpha=0.3)

                        self._rectangleStats.direction = 1
                        ax.add_patch(self._rectangle_interactions)
                    #If it has been created and goes from left to right, update width
                    elif isinstance(
                            self._rectangle_interactions,
                            Rectangle) and self._rectangleStats.direction == 1:

                        self._rectangle_interactions.set_height(
                            abs(yAxis_limits[0] * 0.9 - yAxis_limits[1] * 1.1))
                        self._rectangle_interactions.set_y(yAxis_limits[0] *
                                                           0.9)
                        self._rectangle_interactions.set_x(past_event.xdata)
                        self._rectangle_interactions.set_width(
                            abs(past_event.xdata - curr_event.xdata))

                    #If it has been created but changed direction from right-left to left-right, update width
                    elif self._rectangleStats.direction == -1:

                        self._rectangle_interactions.set_height(
                            abs(yAxis_limits[0] * 0.9 - yAxis_limits[1] * 1.1))
                        self._rectangle_interactions.set_y(yAxis_limits[0] *
                                                           0.9)
                        self._rectangleStats.direction = 1
                        self._rectangle_interactions.set_x(past_event.xdata)
                        self._rectangle_interactions.set_width(
                            abs(past_event.xdata - curr_event.xdata))

        except Exception as e:
            pass

    def _rectangle_creation(self, event):
        """Execute function based on the name of it"""
        if event.name == 'button_press_event':  # begin creation
            self._original_event = event
            self._event = event

        elif event.name == 'button_release_event':  # end creation
            self._event = None
            pub.sendMessage('rangeData',
                            iw=self._rectangle_interactions.get_x(),
                            ew=self._rectangle_interactions.get_bbox().x1)

        elif event.name == 'motion_notify_event':  # set range for creation
            if self._event is None:
                return

            if event.x != self._event.x:
                for ax in self._axes[0]:
                    self.update_current_rectangle(ax, event,
                                                  self._original_event)

            if event.x != self._event.x:
                self._draw()

            self._event = event

    def _on_mouse_press_creation(self, event):
        """Set axes values based on point selected"""
        if self._pressed_button is not None:
            return  # Discard event if a button is already pressed

        x_axes = set()
        y_axes = set()
        for ax in self.figure.axes:
            #Simmilar to event.inaxis = axis
            if ax.contains(event)[0]:
                x_axes.add(ax)
                y_axes.add(ax)
                self._axes = x_axes, y_axes
                self._pressed_button = event.button
                if self._pressed_button == 1:
                    self._rectangle_creation(event)

    def _on_mouse_release_creation(self, event):
        if self._pressed_button == 1:
            self._rectangle_creation(event)

        self._pressed_button = None

    def _on_mouse_motion_creation(self, event):
        if self._pressed_button == 1:
            self._rectangle_creation(event)
                            xyB=(1, 1),
                            coordsA=coords1,
                            coordsB="axes fraction",
                            axesA=gax,
                            axesB=ax2,
                            arrowstyle="-",
                            color="r")

ax2.add_artist(con_axes0)
ax2.add_artist(con_axes1)

ax0 = fig.add_axes(
    [0.7, 0.12, 0.25, 0.25]
)  #inset_axes(fig, width="25%", height="25%", loc=4)##([0.7,0.17,0.25,0.25])#([0.64,0.2,.25,.25])

rectbbox = rect.get_bbox()
rect_xy0 = np.array((rectbbox.x0, rectbbox.y1))  #data coords
rect_xy1 = np.array((rectbbox.x1, rectbbox.y0))  #data coords

ax0_pos = ax0.get_position()
ax_xy0 = np.array(
    (ax0_pos.x0, ax0_pos.y0))  #(0.68,0.11)#ax0_pos.bounds[:2] #axes coords
ax_xy1 = np.array(
    (ax0_pos.x1, ax0_pos.y1))  #(0.68,0.11)#ax0_pos.bounds[:2] #axes coords

con_axes2 = ConnectionPatch(xyA=rect_xy0,
                            coordsA="data",
                            xyB=ax_xy0,
                            coordsB=ax2.transAxes,
                            color='w',
                            arrowstyle='-')
Exemple #6
0
class Brusher():
    '''
    A generic brusher.
    
    Expects:
    fig:  a mpl figure with subplots
      NOTE: each individual point, in each subplot, must be assigned its own color.
            e.g. use `scatter(x_coords, y_coords, c=array_of_rgba_colors)`
    data: a tuple of numpy arrays, with one entry for each axis in fig, in order (i.e. left->right then top->bottom)
      each item in the tuple must by a 2d numpy array with:
      array[0] --> 1d array of x coordinates
      array[1] --> 1d array of y coordinates
      and each list must be in the same order.
      i.e. the nth pair of values in the first array of the tuple must correspond
           to the nth pair of values in every other array in the tuple.
    '''
    def __init__(self, fig, data):
        '''
        fig:  a pre-rendered figure, with subplot axes
        data: the data represented in fig, formatted as described above
        '''
        self.fig = fig
        self.data = data
        self.axl = fig.get_axes()

        # internal things to keep track of
        self.button_down = False
        self.brush = None
        self.brush_axis = None
        self.start_xy = (None, None)

        # define the colors we want to use (overwrites original figure's colors)
        self.unbrushed_color = [.5, .5, .5, .5]  # this is a transparent gray
        self.brushed_color = [0., 0., 1., .75]  # this is a transparent blue

        # register events to track
        self.fig.canvas.mpl_connect('button_press_event', self.button_press)
        self.fig.canvas.mpl_connect('motion_notify_event', self.mouse_motion)
        self.fig.canvas.mpl_connect('button_release_event',
                                    self.button_release)

    def button_press(self, event):
        ''' handles a mouse click '''
        # if clicked outside of axes, ignore
        if not event.inaxes: return

        # remove old brush object if it's there
        if self.brush:
            self.brush.remove()
            self.unbrush_me()
            plt.draw()

        # start a brush here
        self.button_down = True
        self.start_xy = (event.xdata, event.ydata)
        self.brush = Rectangle(self.start_xy,
                               0,
                               0,
                               facecolor='gray',
                               alpha=.15)
        self.brush_axis = event.inaxes

        # add brush to axis
        event.inaxes.add_patch(self.brush)
        plt.draw()

    def mouse_motion(self, event):
        ''' handles the mouse moving around '''
        # if we haven't already clicked and started a brush, ignore the motion
        if not (self.button_down and self.brush): return
        # if we've moved the mouse outside the starting axis, ignore
        if self.brush_axis != event.inaxes: return

        # otherwise, track the motion and update the brush
        self.brush.set_width(abs(self.start_xy[0] - event.xdata))
        self.brush.set_height(abs(self.start_xy[1] - event.ydata))
        self.brush.set_x(min(self.start_xy[0], event.xdata))
        self.brush.set_y(min(self.start_xy[1], event.ydata))
        plt.draw()

    def button_release(self, event):
        ''' handles a mouse button release '''
        self.button_down = False

        # if we didn't start with a brush, ignore
        if not self.brush: return

        # if we just clicked without drawing, remove the brush
        if (event.xdata, event.ydata) == self.start_xy:
            self.unbrush_me()
            plt.draw()
            return

        # if we stopped inside the same axis, update the brush one last time
        if event.inaxes == self.brush_axis:
            self.brush.set_width(abs(self.start_xy[0] - event.xdata))
            self.brush.set_height(abs(self.start_xy[1] - event.ydata))
            self.brush.set_x(min(self.start_xy[0], event.xdata))
            self.brush.set_y(min(self.start_xy[1], event.ydata))

        self.brush_me()
        plt.draw()

    def brush_me(self):
        ''' apply the new brush to data in all axes '''
        # figure out the indices of the data inside the brush
        i_brush_axis = self.axl.index(self.brush_axis)
        ax_data = self.data[i_brush_axis]
        bbox = self.brush.get_bbox()
        boolean_brushed = []
        for i in range(len(ax_data[0])):
            if bbox.contains(ax_data[0, i], ax_data[1, i]):
                boolean_brushed.append(True)
            else:
                boolean_brushed.append(False)
        boolean_brushed = np.array(boolean_brushed)
        # boolean_brushed is an array of T,F values indicating whether the datapoint at
        #  that index should be 'brushed' (i.e. keep its color) or not (made gray)

        for i, ax in enumerate(self.axl):
            plotted_things = ax.collections[0]
            # now set the facecolor of all points not brushed to gray
            #  new_fc is a 2d array, the 0th axis corresponding to individual points,
            #  and the 1st axis corresponding to color values
            new_fc = plotted_things.get_facecolors()
            new_fc[~boolean_brushed] = self.unbrushed_color
            new_fc[boolean_brushed] = self.brushed_color

    def unbrush_me(self):
        ''' paint everything the brushed color '''
        # build an always-true boolean array and brush to that
        boolean_brushed = []
        for i in range(len(self.data[0][0])):
            boolean_brushed.append(True)
        boolean_brushed = np.array(boolean_brushed)
        for i, ax in enumerate(self.axl):
            plotted_things = ax.collections[0]
            new_fc = plotted_things.get_facecolors()
            new_fc[boolean_brushed] = self.brushed_color