Exemplo n.º 1
0
    def __init__(self, ax, on_move=None, on_release=None, on_enter=None, maxdist=10, rect_props=None):
        CanvasToolBase.__init__(self, ax, on_move=on_move, on_enter=on_enter, on_release=on_release)

        props = dict(edgecolor=None, facecolor="r", alpha=0.15)
        props.update(rect_props if rect_props is not None else {})
        if props["edgecolor"] is None:
            props["edgecolor"] = props["facecolor"]
        RectangleSelector.__init__(self, ax, lambda *args: None, rectprops=props, useblit=self.useblit)
        # Alias rectangle attribute, which is initialized in RectangleSelector.
        self._rect = self.to_draw
        self._rect.set_animated(True)

        self.maxdist = maxdist
        self.active_handle = None
        self._extents_on_press = None

        if on_enter is None:

            def on_enter(extents):
                print("(xmin=%.3g, xmax=%.3g, ymin=%.3g, ymax=%.3g)" % extents)

        self.callback_on_enter = on_enter

        props = dict(mec=props["edgecolor"])
        self._corner_order = ["NW", "NE", "SE", "SW"]
        xc, yc = self.corners
        self._corner_handles = ToolHandles(ax, xc, yc, marker_props=props)

        self._edge_order = ["W", "N", "E", "S"]
        xe, ye = self.edge_centers
        self._edge_handles = ToolHandles(ax, xe, ye, marker="s", marker_props=props)

        self._artists = [self._rect, self._corner_handles.artist, self._edge_handles.artist]
Exemplo n.º 2
0
    def __init__(self,
                 viewer,
                 on_move=None,
                 on_release=None,
                 on_enter=None,
                 maxdist=10,
                 rect_props=None):
        CanvasToolBase.__init__(self,
                                viewer,
                                on_move=on_move,
                                on_enter=on_enter,
                                on_release=on_release)

        props = dict(edgecolor=None, facecolor='r', alpha=0.15)
        props.update(rect_props if rect_props is not None else {})
        if props['edgecolor'] is None:
            props['edgecolor'] = props['facecolor']
        RectangleSelector.__init__(self,
                                   self.ax,
                                   lambda *args: None,
                                   rectprops=props)
        self.disconnect_events()  # events are handled by the viewer
        # Alias rectangle attribute, which is initialized in RectangleSelector.
        self._rect = self.to_draw
        self._rect.set_animated(True)

        self.maxdist = maxdist
        self.active_handle = None
        self._extents_on_press = None

        if on_enter is None:

            def on_enter(extents):
                print("(xmin=%.3g, xmax=%.3g, ymin=%.3g, ymax=%.3g)" % extents)

        self.callback_on_enter = on_enter

        props = dict(mec=props['edgecolor'])
        self._corner_order = ['NW', 'NE', 'SE', 'SW']
        xc, yc = self.corners
        self._corner_handles = ToolHandles(self.ax, xc, yc, marker_props=props)

        self._edge_order = ['W', 'N', 'E', 'S']
        xe, ye = self.edge_centers
        self._edge_handles = ToolHandles(self.ax,
                                         xe,
                                         ye,
                                         marker='s',
                                         marker_props=props)

        self.artists = [
            self._rect, self._corner_handles.artist, self._edge_handles.artist
        ]
        viewer.add_tool(self)
Exemplo n.º 3
0
    def __init__(self, viewer, on_move=None, on_release=None, on_enter=None,
                 maxdist=10, line_props=None, handle_props=None,
                 **kwargs):
        super(LineTool, self).__init__(viewer, on_move=on_move,
                                       on_enter=on_enter,
                                       on_release=on_release, **kwargs)

        props = dict(color='r', linewidth=1, alpha=0.4, solid_capstyle='butt')
        props.update(line_props if line_props is not None else {})
        self.linewidth = props['linewidth']
        self.maxdist = maxdist
        self._active_pt = None

        x = (0, 0)
        y = (0, 0)
        self._end_pts = np.transpose([x, y])

        self._line = lines.Line2D(x, y, visible=False, animated=True, **props)
        self.ax.add_line(self._line)

        self._handles = ToolHandles(self.ax, x, y,
                                    marker_props=handle_props)
        self._handles.set_visible(False)
        self.artists = [self._line, self._handles.artist]

        if on_enter is None:
            def on_enter(pts):
                x, y = np.transpose(pts)
                print("length = %0.2f" %
                      np.sqrt(np.diff(x)**2 + np.diff(y)**2))
        self.callback_on_enter = on_enter
        viewer.add_tool(self)
Exemplo n.º 4
0
    def __init__(self, ax, on_move=None, on_release=None, on_enter=None,
                 maxdist=10, line_props=None):
        super(LineToolC, self).__init__(ax, on_move=on_move, on_enter=on_enter,
                                       on_release=on_release)

        props = dict(color='r', linewidth=1, alpha=0.25, solid_capstyle='butt')
        props.update(line_props if line_props is not None else {})
        self.linewidth = props['linewidth']
        self.maxdist = 10
        self.position = 'blah'
        self._active_pt = None

        x = (0, 0)
        y = (0, 0)
        self._end_pts = np.transpose([x, y])

        self._line = lines.Line2D(x, y, visible=False, animated=True, **props)
        ax.add_line(self._line)

        self._handles = ToolHandles(ax, x, y)
        self._handles.set_visible(False)
        self._artists = [self._line, self._handles.artist]

        if on_enter is None:
            def on_enter(pts):
                x, y = np.transpose(pts)
                print("length = %0.2f" % np.sqrt(np.diff(x)**2 + np.diff(y)**2))
        self.callback_on_enter = on_enter

        self.connect_event('button_press_event', self.on_mouse_press)
        self.connect_event('button_release_event', self.on_mouse_release)
        self.connect_event('motion_notify_event', self.on_move)
Exemplo n.º 5
0
    def __init__(self, viewer, on_move=None, on_release=None, on_enter=None,
                 maxdist=10, rect_props=None):
        self._rect = None
        props = dict(edgecolor=None, facecolor='r', alpha=0.15)
        props.update(rect_props if rect_props is not None else {})
        if props['edgecolor'] is None:
            props['edgecolor'] = props['facecolor']
        RectangleSelector.__init__(self, viewer.ax, lambda *args: None,
                                   rectprops=props)
        CanvasToolBase.__init__(self, viewer, on_move=on_move,
                                on_enter=on_enter, on_release=on_release)

        # Events are handled by the viewer
        try:
            self.disconnect_events()
        except AttributeError:
            # disconnect the events manually (hack for older mpl versions)
            [self.canvas.mpl_disconnect(i) for i in range(10)]

        # Alias rectangle attribute, which is initialized in RectangleSelector.
        self._rect = self.to_draw
        self._rect.set_animated(True)

        self.maxdist = maxdist
        self.active_handle = None
        self._extents_on_press = None

        if on_enter is None:
            def on_enter(extents):
                print("(xmin=%.3g, xmax=%.3g, ymin=%.3g, ymax=%.3g)" % extents)
        self.callback_on_enter = on_enter

        props = dict(mec=props['edgecolor'])
        self._corner_order = ['NW', 'NE', 'SE', 'SW']
        xc, yc = self.corners
        self._corner_handles = ToolHandles(self.ax, xc, yc, marker_props=props)

        self._edge_order = ['W', 'N', 'E', 'S']
        xe, ye = self.edge_centers
        self._edge_handles = ToolHandles(self.ax, xe, ye, marker='s',
                                         marker_props=props)

        self.artists = [self._rect,
                        self._corner_handles.artist,
                        self._edge_handles.artist]
        viewer.add_tool(self)
Exemplo n.º 6
0
    def __init__(self,
                 viewer,
                 on_move=None,
                 on_release=None,
                 on_enter=None,
                 maxdist=10,
                 line_props=None,
                 handle_props=None,
                 **kwargs):
        super(LineTool, self).__init__(viewer,
                                       on_move=on_move,
                                       on_enter=on_enter,
                                       on_release=on_release,
                                       **kwargs)

        props = dict(color='r', linewidth=1, alpha=0.4, solid_capstyle='butt')
        props.update(line_props if line_props is not None else {})
        self.linewidth = props['linewidth']
        self.maxdist = maxdist
        self._active_pt = None

        x = (0, 0)
        y = (0, 0)
        self._end_pts = np.transpose([x, y])

        self._line = lines.Line2D(x, y, visible=False, animated=True, **props)
        self.ax.add_line(self._line)

        self._handles = ToolHandles(self.ax, x, y, marker_props=handle_props)
        self._handles.set_visible(False)
        self.artists = [self._line, self._handles.artist]

        if on_enter is None:

            def on_enter(pts):
                x, y = np.transpose(pts)
                print("length = %0.2f" %
                      np.sqrt(np.diff(x)**2 + np.diff(y)**2))

        self.callback_on_enter = on_enter
        viewer.add_tool(self)
Exemplo n.º 7
0
class RectangleTool(CanvasToolBase, RectangleSelector):
    """Widget for selecting a rectangular region in a plot.

    After making the desired selection, press "Enter" to accept the selection
    and call the `on_enter` callback function.

    Parameters
    ----------
    viewer : :class:`skimage.viewer.Viewer`
        Skimage viewer object.
    on_move : function
        Function called whenever a control handle is moved.
        This function must accept the rectangle extents as the only argument.
    on_release : function
        Function called whenever the control handle is released.
    on_enter : function
        Function called whenever the "enter" key is pressed.
    maxdist : float
        Maximum pixel distance allowed when selecting control handle.
    rect_props : dict
        Properties for :class:`matplotlib.patches.Rectangle`. This class
        redefines defaults in :class:`matplotlib.widgets.RectangleSelector`.

    Attributes
    ----------
    extents : tuple
        Rectangle extents: (xmin, xmax, ymin, ymax).
    """
    def __init__(self,
                 viewer,
                 on_move=None,
                 on_release=None,
                 on_enter=None,
                 maxdist=10,
                 rect_props=None):
        CanvasToolBase.__init__(self,
                                viewer,
                                on_move=on_move,
                                on_enter=on_enter,
                                on_release=on_release)

        props = dict(edgecolor=None, facecolor='r', alpha=0.15)
        props.update(rect_props if rect_props is not None else {})
        if props['edgecolor'] is None:
            props['edgecolor'] = props['facecolor']
        RectangleSelector.__init__(self,
                                   self.ax,
                                   lambda *args: None,
                                   rectprops=props)
        self.disconnect_events()  # events are handled by the viewer
        # Alias rectangle attribute, which is initialized in RectangleSelector.
        self._rect = self.to_draw
        self._rect.set_animated(True)

        self.maxdist = maxdist
        self.active_handle = None
        self._extents_on_press = None

        if on_enter is None:

            def on_enter(extents):
                print("(xmin=%.3g, xmax=%.3g, ymin=%.3g, ymax=%.3g)" % extents)

        self.callback_on_enter = on_enter

        props = dict(mec=props['edgecolor'])
        self._corner_order = ['NW', 'NE', 'SE', 'SW']
        xc, yc = self.corners
        self._corner_handles = ToolHandles(self.ax, xc, yc, marker_props=props)

        self._edge_order = ['W', 'N', 'E', 'S']
        xe, ye = self.edge_centers
        self._edge_handles = ToolHandles(self.ax,
                                         xe,
                                         ye,
                                         marker='s',
                                         marker_props=props)

        self.artists = [
            self._rect, self._corner_handles.artist, self._edge_handles.artist
        ]
        viewer.add_tool(self)

    @property
    def _rect_bbox(self):
        x0 = self._rect.get_x()
        y0 = self._rect.get_y()
        width = self._rect.get_width()
        height = self._rect.get_height()
        return x0, y0, width, height

    @property
    def corners(self):
        """Corners of rectangle from lower left, moving clockwise."""
        x0, y0, width, height = self._rect_bbox
        xc = x0, x0 + width, x0 + width, x0
        yc = y0, y0, y0 + height, y0 + height
        return xc, yc

    @property
    def edge_centers(self):
        """Midpoint of rectangle edges from left, moving clockwise."""
        x0, y0, width, height = self._rect_bbox
        w = width / 2.
        h = height / 2.
        xe = x0, x0 + w, x0 + width, x0 + w
        ye = y0 + h, y0, y0 + h, y0 + height
        return xe, ye

    @property
    def extents(self):
        """Return (xmin, xmax, ymin, ymax)."""
        x0, y0, width, height = self._rect_bbox
        xmin, xmax = sorted([x0, x0 + width])
        ymin, ymax = sorted([y0, y0 + height])
        return xmin, xmax, ymin, ymax

    @extents.setter
    def extents(self, extents):
        x1, x2, y1, y2 = extents
        xmin, xmax = sorted([x1, x2])
        ymin, ymax = sorted([y1, y2])
        # Update displayed rectangle
        self._rect.set_x(xmin)
        self._rect.set_y(ymin)
        self._rect.set_width(xmax - xmin)
        self._rect.set_height(ymax - ymin)
        # Update displayed handles
        self._corner_handles.set_data(*self.corners)
        self._edge_handles.set_data(*self.edge_centers)

        self.set_visible(True)
        self.redraw()

    def on_mouse_release(self, event):
        if event.button != 1:
            return
        if not self.ax.in_axes(event):
            self.eventpress = None
            return
        RectangleSelector.release(self, event)
        self._extents_on_press = None
        # Undo hiding of rectangle and redraw.
        self.set_visible(True)
        self.redraw()
        self.callback_on_release(self.geometry)

    def on_mouse_press(self, event):
        if event.button != 1 or not self.ax.in_axes(event):
            return
        self._set_active_handle(event)
        if self.active_handle is None:
            # Clear previous rectangle before drawing new rectangle.
            self.set_visible(False)
            self.redraw()
        self.set_visible(True)
        RectangleSelector.press(self, event)

    def _set_active_handle(self, event):
        """Set active handle based on the location of the mouse event"""
        # Note: event.xdata/ydata in data coordinates, event.x/y in pixels
        c_idx, c_dist = self._corner_handles.closest(event.x, event.y)
        e_idx, e_dist = self._edge_handles.closest(event.x, event.y)

        # Set active handle as closest handle, if mouse click is close enough.
        if c_dist > self.maxdist and e_dist > self.maxdist:
            self.active_handle = None
            return
        elif c_dist < e_dist:
            self.active_handle = self._corner_order[c_idx]
        else:
            self.active_handle = self._edge_order[e_idx]

        # Save coordinates of rectangle at the start of handle movement.
        x1, x2, y1, y2 = self.extents
        # Switch variables so that only x2 and/or y2 are updated on move.
        if self.active_handle in ['W', 'SW', 'NW']:
            x1, x2 = x2, event.xdata
        if self.active_handle in ['N', 'NW', 'NE']:
            y1, y2 = y2, event.ydata
        self._extents_on_press = x1, x2, y1, y2

    def on_move(self, event):
        if self.eventpress is None or not self.ax.in_axes(event):
            return

        if self.active_handle is None:
            # New rectangle
            x1 = self.eventpress.xdata
            y1 = self.eventpress.ydata
            x2, y2 = event.xdata, event.ydata
        else:
            x1, x2, y1, y2 = self._extents_on_press
            if self.active_handle in ['E', 'W'] + self._corner_order:
                x2 = event.xdata
            if self.active_handle in ['N', 'S'] + self._corner_order:
                y2 = event.ydata
        self.extents = (x1, x2, y1, y2)
        self.callback_on_move(self.geometry)

    @property
    def geometry(self):
        return self.extents
Exemplo n.º 8
0
class RectangleTool(CanvasToolBase, RectangleSelector):
    """Widget for selecting a rectangular region in a plot.

    After making the desired selection, press "Enter" to accept the selection
    and call the `on_enter` callback function.

    Parameters
    ----------
    viewer : :class:`skimage.viewer.Viewer`
        Skimage viewer object.
    on_move : function
        Function called whenever a control handle is moved.
        This function must accept the rectangle extents as the only argument.
    on_release : function
        Function called whenever the control handle is released.
    on_enter : function
        Function called whenever the "enter" key is pressed.
    maxdist : float
        Maximum pixel distance allowed when selecting control handle.
    rect_props : dict
        Properties for :class:`matplotlib.patches.Rectangle`. This class
        redefines defaults in :class:`matplotlib.widgets.RectangleSelector`.

    Attributes
    ----------
    extents : tuple
        Rectangle extents: (xmin, xmax, ymin, ymax).
    
    Examples
    ----------
    >>> from skimage import data
    >>> from skimage.viewer import ImageViewer
    >>> from skimage.viewer.canvastools import RectangleTool
    >>> from skimage.draw import line
    >>> from skimage.draw import set_color
 
    >>> viewer = ImageViewer(data.coffee())  # doctest: +SKIP

    >>> def print_the_rect(extents):
    ...     global viewer
    ...     im = viewer.image    
    ...     coord = np.int64(extents)
    ...     [rr1, cc1] = line(coord[2],coord[0],coord[2],coord[1])
    ...     [rr2, cc2] = line(coord[2],coord[1],coord[3],coord[1])
    ...     [rr3, cc3] = line(coord[3],coord[1],coord[3],coord[0])
    ...     [rr4, cc4] = line(coord[3],coord[0],coord[2],coord[0])
    ...     set_color(im, (rr1, cc1), [255, 255, 0])
    ...     set_color(im, (rr2, cc2), [0, 255, 255])
    ...     set_color(im, (rr3, cc3), [255, 0, 255])
    ...     set_color(im, (rr4, cc4), [0, 0, 0])
    ...     viewer.image=im
    
    >>> rect_tool = RectangleTool(viewer.ax, on_enter=print_the_rect) # doctest: +SKIP
    >>> viewer.show() # doctest: +SKIP
    """
    
    def __init__(self, viewer, on_move=None, on_release=None, on_enter=None,
                 maxdist=10, rect_props=None):
        self._rect = None
        props = dict(edgecolor=None, facecolor='r', alpha=0.15)
        props.update(rect_props if rect_props is not None else {})
        if props['edgecolor'] is None:
            props['edgecolor'] = props['facecolor']
        RectangleSelector.__init__(self, viewer.ax, lambda *args: None,
                                   rectprops=props)
        CanvasToolBase.__init__(self, viewer, on_move=on_move,
                                on_enter=on_enter, on_release=on_release)

        # Events are handled by the viewer
        try:
            self.disconnect_events()
        except AttributeError:
            # disconnect the events manually (hack for older mpl versions)
            [self.canvas.mpl_disconnect(i) for i in range(10)]

        # Alias rectangle attribute, which is initialized in RectangleSelector.
        self._rect = self.to_draw
        self._rect.set_animated(True)

        self.maxdist = maxdist
        self.active_handle = None
        self._extents_on_press = None

        if on_enter is None:
            def on_enter(extents):
                print("(xmin=%.3g, xmax=%.3g, ymin=%.3g, ymax=%.3g)" % extents)
        self.callback_on_enter = on_enter

        props = dict(mec=props['edgecolor'])
        self._corner_order = ['NW', 'NE', 'SE', 'SW']
        xc, yc = self.corners
        self._corner_handles = ToolHandles(self.ax, xc, yc, marker_props=props)

        self._edge_order = ['W', 'N', 'E', 'S']
        xe, ye = self.edge_centers
        self._edge_handles = ToolHandles(self.ax, xe, ye, marker='s',
                                         marker_props=props)

        self.artists = [self._rect,
                        self._corner_handles.artist,
                        self._edge_handles.artist]
        viewer.add_tool(self)

    @property
    def _rect_bbox(self):
        if not self._rect:
            return 0, 0, 0, 0
        x0 = self._rect.get_x()
        y0 = self._rect.get_y()
        width = self._rect.get_width()
        height = self._rect.get_height()
        return x0, y0, width, height

    @property
    def corners(self):
        """Corners of rectangle from lower left, moving clockwise."""
        x0, y0, width, height = self._rect_bbox
        xc = x0, x0 + width, x0 + width, x0
        yc = y0, y0, y0 + height, y0 + height
        return xc, yc

    @property
    def edge_centers(self):
        """Midpoint of rectangle edges from left, moving clockwise."""
        x0, y0, width, height = self._rect_bbox
        w = width / 2.
        h = height / 2.
        xe = x0, x0 + w, x0 + width, x0 + w
        ye = y0 + h, y0, y0 + h, y0 + height
        return xe, ye

    @property
    def extents(self):
        """Return (xmin, xmax, ymin, ymax)."""
        x0, y0, width, height = self._rect_bbox
        xmin, xmax = sorted([x0, x0 + width])
        ymin, ymax = sorted([y0, y0 + height])
        return xmin, xmax, ymin, ymax

    @extents.setter
    def extents(self, extents):
        x1, x2, y1, y2 = extents
        xmin, xmax = sorted([x1, x2])
        ymin, ymax = sorted([y1, y2])
        # Update displayed rectangle
        self._rect.set_x(xmin)
        self._rect.set_y(ymin)
        self._rect.set_width(xmax - xmin)
        self._rect.set_height(ymax - ymin)
        # Update displayed handles
        self._corner_handles.set_data(*self.corners)
        self._edge_handles.set_data(*self.edge_centers)

        self.set_visible(True)
        self.redraw()

    def on_mouse_release(self, event):
        if event.button != 1:
            return
        if not self.ax.in_axes(event):
            self.eventpress = None
            return
        RectangleSelector.release(self, event)
        self._extents_on_press = None
        # Undo hiding of rectangle and redraw.
        self.set_visible(True)
        self.redraw()
        self.callback_on_release(self.geometry)

    def on_mouse_press(self, event):
        if event.button != 1 or not self.ax.in_axes(event):
            return
        self._set_active_handle(event)
        if self.active_handle is None:
            # Clear previous rectangle before drawing new rectangle.
            self.set_visible(False)
            self.redraw()
        self.set_visible(True)
        RectangleSelector.press(self, event)

    def _set_active_handle(self, event):
        """Set active handle based on the location of the mouse event"""
        # Note: event.xdata/ydata in data coordinates, event.x/y in pixels
        c_idx, c_dist = self._corner_handles.closest(event.x, event.y)
        e_idx, e_dist = self._edge_handles.closest(event.x, event.y)

        # Set active handle as closest handle, if mouse click is close enough.
        if c_dist > self.maxdist and e_dist > self.maxdist:
            self.active_handle = None
            return
        elif c_dist < e_dist:
            self.active_handle = self._corner_order[c_idx]
        else:
            self.active_handle = self._edge_order[e_idx]

        # Save coordinates of rectangle at the start of handle movement.
        x1, x2, y1, y2 = self.extents
        # Switch variables so that only x2 and/or y2 are updated on move.
        if self.active_handle in ['W', 'SW', 'NW']:
            x1, x2 = x2, event.xdata
        if self.active_handle in ['N', 'NW', 'NE']:
            y1, y2 = y2, event.ydata
        self._extents_on_press = x1, x2, y1, y2

    def on_move(self, event):
        if self.eventpress is None or not self.ax.in_axes(event):
            return

        if self.active_handle is None:
            # New rectangle
            x1 = self.eventpress.xdata
            y1 = self.eventpress.ydata
            x2, y2 = event.xdata, event.ydata
        else:
            x1, x2, y1, y2 = self._extents_on_press
            if self.active_handle in ['E', 'W'] + self._corner_order:
                x2 = event.xdata
            if self.active_handle in ['N', 'S'] + self._corner_order:
                y2 = event.ydata
        self.extents = (x1, x2, y1, y2)
        self.callback_on_move(self.geometry)

    @property
    def geometry(self):
        return self.extents
Exemplo n.º 9
0
class LineTool(CanvasToolBase):
    """Widget for line selection in a plot.

    Parameters
    ----------
    viewer : :class:`skimage.viewer.Viewer`
        Skimage viewer object.
    on_move : function
        Function called whenever a control handle is moved.
        This function must accept the end points of line as the only argument.
    on_release : function
        Function called whenever the control handle is released.
    on_enter : function
        Function called whenever the "enter" key is pressed.
    maxdist : float
        Maximum pixel distance allowed when selecting control handle.
    line_props : dict
        Properties for :class:`matplotlib.lines.Line2D`.
    handle_props : dict
        Marker properties for the handles (also see
        :class:`matplotlib.lines.Line2D`).

    Attributes
    ----------
    end_points : 2D array
        End points of line ((x1, y1), (x2, y2)).
    """
    def __init__(self,
                 viewer,
                 on_move=None,
                 on_release=None,
                 on_enter=None,
                 maxdist=10,
                 line_props=None,
                 handle_props=None,
                 **kwargs):
        super(LineTool, self).__init__(viewer,
                                       on_move=on_move,
                                       on_enter=on_enter,
                                       on_release=on_release,
                                       **kwargs)

        props = dict(color='r', linewidth=1, alpha=0.4, solid_capstyle='butt')
        props.update(line_props if line_props is not None else {})
        self.linewidth = props['linewidth']
        self.maxdist = maxdist
        self._active_pt = None

        x = (0, 0)
        y = (0, 0)
        self._end_pts = np.transpose([x, y])

        self._line = lines.Line2D(x, y, visible=False, animated=True, **props)
        self.ax.add_line(self._line)

        self._handles = ToolHandles(self.ax, x, y, marker_props=handle_props)
        self._handles.set_visible(False)
        self.artists = [self._line, self._handles.artist]

        if on_enter is None:

            def on_enter(pts):
                x, y = np.transpose(pts)
                print("length = %0.2f" %
                      np.sqrt(np.diff(x)**2 + np.diff(y)**2))

        self.callback_on_enter = on_enter
        viewer.add_tool(self)

    @property
    def end_points(self):
        return self._end_pts.astype(int)

    @end_points.setter
    def end_points(self, pts):
        self._end_pts = np.asarray(pts)

        self._line.set_data(np.transpose(pts))
        self._handles.set_data(np.transpose(pts))
        self._line.set_linewidth(self.linewidth)

        self.set_visible(True)
        self.redraw()

    def hit_test(self, event):
        if event.button != 1 or not self.ax.in_axes(event):
            return False
        idx, px_dist = self._handles.closest(event.x, event.y)
        if px_dist < self.maxdist:
            self._active_pt = idx
            return True
        else:
            self._active_pt = None
            return False

    def on_mouse_press(self, event):
        self.set_visible(True)
        if self._active_pt is None:
            self._active_pt = 0
            x, y = event.xdata, event.ydata
            self._end_pts = np.array([[x, y], [x, y]])

    def on_mouse_release(self, event):
        if event.button != 1:
            return
        self._active_pt = None
        self.callback_on_release(self.geometry)
        self.redraw()

    def on_move(self, event):
        if event.button != 1 or self._active_pt is None:
            return
        if not self.ax.in_axes(event):
            return
        self.update(event.xdata, event.ydata)
        self.callback_on_move(self.geometry)

    def update(self, x=None, y=None):
        if x is not None:
            self._end_pts[self._active_pt, :] = x, y
        self.end_points = self._end_pts

    @property
    def geometry(self):
        return self.end_points
Exemplo n.º 10
0
class LineTool(CanvasToolBase):
    """Widget for line selection in a plot.

    Parameters
    ----------
    viewer : :class:`skimage.viewer.Viewer`
        Skimage viewer object.
    on_move : function
        Function called whenever a control handle is moved.
        This function must accept the end points of line as the only argument.
    on_release : function
        Function called whenever the control handle is released.
    on_enter : function
        Function called whenever the "enter" key is pressed.
    maxdist : float
        Maximum pixel distance allowed when selecting control handle.
    line_props : dict
        Properties for :class:`matplotlib.lines.Line2D`.
    handle_props : dict
        Marker properties for the handles (also see
        :class:`matplotlib.lines.Line2D`).

    Attributes
    ----------
    end_points : 2D array
        End points of line ((x1, y1), (x2, y2)).
    """
    def __init__(self, viewer, on_move=None, on_release=None, on_enter=None,
                 maxdist=10, line_props=None, handle_props=None,
                 **kwargs):
        super(LineTool, self).__init__(viewer, on_move=on_move,
                                       on_enter=on_enter,
                                       on_release=on_release, **kwargs)

        props = dict(color='r', linewidth=1, alpha=0.4, solid_capstyle='butt')
        props.update(line_props if line_props is not None else {})
        self.linewidth = props['linewidth']
        self.maxdist = maxdist
        self._active_pt = None

        x = (0, 0)
        y = (0, 0)
        self._end_pts = np.transpose([x, y])

        self._line = lines.Line2D(x, y, visible=False, animated=True, **props)
        self.ax.add_line(self._line)

        self._handles = ToolHandles(self.ax, x, y,
                                    marker_props=handle_props)
        self._handles.set_visible(False)
        self.artists = [self._line, self._handles.artist]

        if on_enter is None:
            def on_enter(pts):
                x, y = np.transpose(pts)
                print("length = %0.2f" %
                      np.sqrt(np.diff(x)**2 + np.diff(y)**2))
        self.callback_on_enter = on_enter
        viewer.add_tool(self)

    @property
    def end_points(self):
        return self._end_pts.astype(int)

    @end_points.setter
    def end_points(self, pts):
        self._end_pts = np.asarray(pts)

        self._line.set_data(np.transpose(pts))
        self._handles.set_data(np.transpose(pts))
        self._line.set_linewidth(self.linewidth)

        self.set_visible(True)
        self.redraw()

    def hit_test(self, event):
        if event.button != 1 or not self.ax.in_axes(event):
            return False
        idx, px_dist = self._handles.closest(event.x, event.y)
        if px_dist < self.maxdist:
            self._active_pt = idx
            return True
        else:
            self._active_pt = None
            return False

    def on_mouse_press(self, event):
        self.set_visible(True)
        if self._active_pt is None:
            self._active_pt = 0
            x, y = event.xdata, event.ydata
            self._end_pts = np.array([[x, y], [x, y]])

    def on_mouse_release(self, event):
        if event.button != 1:
            return
        self._active_pt = None
        self.callback_on_release(self.geometry)
        self.redraw()

    def on_move(self, event):
        if event.button != 1 or self._active_pt is None:
            return
        if not self.ax.in_axes(event):
            return
        self.update(event.xdata, event.ydata)
        self.callback_on_move(self.geometry)

    def update(self, x=None, y=None):
        if x is not None:
            self._end_pts[self._active_pt, :] = x, y
        self.end_points = self._end_pts

    @property
    def geometry(self):
        return self.end_points
Exemplo n.º 11
0
class LineToolC(CanvasToolBase):
    """Widget for line selection in a plot.

    Parameters
    ----------
    ax : :class:`matplotlib.axes.Axes`
        Matplotlib axes where tool is displayed.
    on_move : function
        Function called whenever a control handle is moved.
        This function must accept the end points of line as the only argument.
    on_release : function
        Function called whenever the control handle is released.
    on_enter : function
        Function called whenever the "enter" key is pressed.
    maxdist : float
        Maximum pixel distance allowed when selecting control handle.
    line_props : dict
        Properties for :class:`matplotlib.lines.Line2D`.

    Attributes
    ----------
    end_points : 2D array
        End points of line ((x1, y1), (x2, y2)).
    """
    def __init__(self, ax, on_move=None, on_release=None, on_enter=None,
                 maxdist=10, line_props=None):
        super(LineToolC, self).__init__(ax, on_move=on_move, on_enter=on_enter,
                                       on_release=on_release)

        props = dict(color='r', linewidth=1, alpha=0.25, solid_capstyle='butt')
        props.update(line_props if line_props is not None else {})
        self.linewidth = props['linewidth']
        self.maxdist = 10
        self.position = 'blah'
        self._active_pt = None

        x = (0, 0)
        y = (0, 0)
        self._end_pts = np.transpose([x, y])

        self._line = lines.Line2D(x, y, visible=False, animated=True, **props)
        ax.add_line(self._line)

        self._handles = ToolHandles(ax, x, y)
        self._handles.set_visible(False)
        self._artists = [self._line, self._handles.artist]

        if on_enter is None:
            def on_enter(pts):
                x, y = np.transpose(pts)
                print("length = %0.2f" % np.sqrt(np.diff(x)**2 + np.diff(y)**2))
        self.callback_on_enter = on_enter

        self.connect_event('button_press_event', self.on_mouse_press)
        self.connect_event('button_release_event', self.on_mouse_release)
        self.connect_event('motion_notify_event', self.on_move)

    @property
    def end_points(self):
        return self._end_pts

    @end_points.setter
    def end_points(self, pts):
        self._end_pts = np.asarray(pts)
        self._line.set_data(np.transpose(pts))
        self._handles.set_data(np.transpose(pts))
        self._line.set_linewidth(self.linewidth)

        self.set_visible(True)
        self.redraw()

    def on_mouse_press(self, event):
        if event.button != 1 or not self.ax.in_axes(event):
            return
        self.set_visible(True)
        idx, px_dist = self._handles.closest(event.x, event.y)
        if px_dist < self.maxdist:
            self._active_pt = idx
#        else:
#            self._active_pt = 0
#            x, y = event.xdata, event.ydata
#            self._end_pts = np.array([[x, y], [x, y]])
    
    def distalong(self, anchors, d):
        x1,y1 = anchors[0].end_points[0]
        x2,y2 = anchors[1].end_points[1]
        m = (y2-y1)/(x2-x1)
        xadj = x1 + math.sqrt((d*d)/(m*m+1))
        yadj = m*(xadj - x1)+y1
        
        return [xadj, yadj]

    def distance(self, xy1, xy2):
        x1,y1 = xy1
        x2,y2 = xy2

        return math.sqrt((y1-y2)**2 + (x1-x2)**2)        
    
    def closest(self, p1, anchors):
        x3,y3 = p1
        x1,y1 = anchors[0].end_points[0]
        x2,y2 = anchors[1].end_points[1]
        
#        print x1,y1,'\n',x2,y2,'\n',x3,y3        
        
        m = (y2-y1)/(x2-x1)
        xadj = (x3+m*(y3-y1)+m*m*x1)/(m*m+1)
        yadj = m*(xadj - x1)+y1
        return [xadj, yadj]

    def interpolate(self, points):
        if self.position == 'anchorright':
            return [self.closest(points[0], self.anchors),points[1]]
        elif self.position == 'anchorleft':
            return [points[0],self.closest(points[1], self.anchors)]
        elif self.position == 'mid':
            return [self.closest(x, self.anchors) for x in points]
        elif self.position == 'midspine':
            return [self.closest(points[0], self.topanchors), self.closest(points[1], self.bottomanchors)]

        return points

    def on_mouse_release(self, event):
        if event.button != 1:
            return
        self._active_pt = None
        self.callback_on_release(self.geometry)

    def on_move(self, event):
#        print 'onmove'
        idx, px_dist = self._handles.closest(event.x, event.y)
#        if px_dist < self.maxdist:
#            print px_dist, self.maxdist
        if event.button != 1 or self._active_pt is None:
            return
        if not self.ax.in_axes(event):
            return
        self.update(event.xdata, event.ydata)
        self.callback_on_move(self.geometry)

    def update(self, x=None, y=None, orig=None):
        if x is not None:
            self._end_pts[self._active_pt, :] = x, y
        
        if orig is None:
            px = np.round(self._end_pts)
            px = self.interpolate(px)
            self._end_pts = np.asarray(px)            
            self.end_points = self._end_pts
        
        
        if orig == None and self.position == 'anchorleft' or self.position == 'anchorright':
            fulld = self.distance(self.anchors[0].end_points[0],self.anchors[1].end_points[1])
            step = fulld / self.ncells
            
            topa = self.midchildren[1].topanchors
            bota = self.midchildren[1].bottomanchors
            
            topd = self.distance(topa[0].end_points[0], topa[1].end_points[1])
            botd = self.distance(bota[0].end_points[0], bota[1].end_points[1])
            
            topstep = topd/self.ncells
            botstep = botd/self.ncells

            for i,x in enumerate(self.midchildren[1:-1]):
                x.end_points = [x.distalong(x.topanchors,topstep*(i+1)), x.distalong(x.bottomanchors, botstep*(i+1))]
            
            if self.position == 'anchorleft' and self._active_pt == 0:
                self.end_points = [self.end_points[0], self.distalong(self.anchors,step)]
                for i,x in enumerate(self.children):
                    if x is not self:
                        if x.position != 'anchorright':
                            x.end_points = [x.distalong(x.anchors,step*i), x.distalong(x.anchors, step*(i+1))]
                        else:
                            x.end_points = [x.distalong(x.anchors,step*i), x.end_points[1]]
#                        x.update(orig = i)
            if self.position == 'anchorright' and self._active_pt == 1:
                self.end_points = [self.distalong(self.anchors,step), self.end_points[1]]
                for i,x in enumerate(self.children):
                    if x is not self:
                        if x.position != 'anchorleft':
                            x.end_points = [x.distalong(x.anchors,step*i), x.distalong(x.anchors, step*(i+1))]
                        else:
                            x.end_points = [x.end_points[0], x.distalong(x.anchors,step)]
#                        x.update(orig = i)

    @property
    def geometry(self):
        return self.end_points
Exemplo n.º 12
0
class RectangleTool(CanvasToolBase, RectangleSelector):
    """Widget for selecting a rectangular region in a plot.

    After making the desired selection, press "Enter" to accept the selection
    and call the `on_enter` callback function.

    Parameters
    ----------
    ax : :class:`matplotlib.axes.Axes`
        Matplotlib axes where tool is displayed.
    on_move : function
        Function called whenever a control handle is moved.
        This function must accept the rectangle extents as the only argument.
    on_release : function
        Function called whenever the control handle is released.
    on_enter : function
        Function called whenever the "enter" key is pressed.
    maxdist : float
        Maximum pixel distance allowed when selecting control handle.
    rect_props : dict
        Properties for :class:`matplotlib.patches.Rectangle`. This class
        redefines defaults in :class:`matplotlib.widgets.RectangleSelector`.

    Attributes
    ----------
    extents : tuple
        Rectangle extents: (xmin, xmax, ymin, ymax).
    """

    def __init__(self, ax, on_move=None, on_release=None, on_enter=None, maxdist=10, rect_props=None):
        CanvasToolBase.__init__(self, ax, on_move=on_move, on_enter=on_enter, on_release=on_release)

        props = dict(edgecolor=None, facecolor="r", alpha=0.15)
        props.update(rect_props if rect_props is not None else {})
        if props["edgecolor"] is None:
            props["edgecolor"] = props["facecolor"]
        RectangleSelector.__init__(self, ax, lambda *args: None, rectprops=props, useblit=self.useblit)
        # Alias rectangle attribute, which is initialized in RectangleSelector.
        self._rect = self.to_draw
        self._rect.set_animated(True)

        self.maxdist = maxdist
        self.active_handle = None
        self._extents_on_press = None

        if on_enter is None:

            def on_enter(extents):
                print("(xmin=%.3g, xmax=%.3g, ymin=%.3g, ymax=%.3g)" % extents)

        self.callback_on_enter = on_enter

        props = dict(mec=props["edgecolor"])
        self._corner_order = ["NW", "NE", "SE", "SW"]
        xc, yc = self.corners
        self._corner_handles = ToolHandles(ax, xc, yc, marker_props=props)

        self._edge_order = ["W", "N", "E", "S"]
        xe, ye = self.edge_centers
        self._edge_handles = ToolHandles(ax, xe, ye, marker="s", marker_props=props)

        self._artists = [self._rect, self._corner_handles.artist, self._edge_handles.artist]

    @property
    def _rect_bbox(self):
        x0 = self._rect.get_x()
        y0 = self._rect.get_y()
        width = self._rect.get_width()
        height = self._rect.get_height()
        return x0, y0, width, height

    @property
    def corners(self):
        """Corners of rectangle from lower left, moving clockwise."""
        x0, y0, width, height = self._rect_bbox
        xc = x0, x0 + width, x0 + width, x0
        yc = y0, y0, y0 + height, y0 + height
        return xc, yc

    @property
    def edge_centers(self):
        """Midpoint of rectangle edges from left, moving clockwise."""
        x0, y0, width, height = self._rect_bbox
        w = width / 2.0
        h = height / 2.0
        xe = x0, x0 + w, x0 + width, x0 + w
        ye = y0 + h, y0, y0 + h, y0 + height
        return xe, ye

    @property
    def extents(self):
        """Return (xmin, xmax, ymin, ymax)."""
        x0, y0, width, height = self._rect_bbox
        xmin, xmax = sorted([x0, x0 + width])
        ymin, ymax = sorted([y0, y0 + height])
        return xmin, xmax, ymin, ymax

    @extents.setter
    def extents(self, extents):
        x1, x2, y1, y2 = extents
        xmin, xmax = sorted([x1, x2])
        ymin, ymax = sorted([y1, y2])
        # Update displayed rectangle
        self._rect.set_x(xmin)
        self._rect.set_y(ymin)
        self._rect.set_width(xmax - xmin)
        self._rect.set_height(ymax - ymin)
        # Update displayed handles
        self._corner_handles.set_data(*self.corners)
        self._edge_handles.set_data(*self.edge_centers)

        self.set_visible(True)
        self.redraw()

    def release(self, event):
        if event.button != 1:
            return
        if not self.ax.in_axes(event):
            self.eventpress = None
            return
        RectangleSelector.release(self, event)
        self._extents_on_press = None
        # Undo hiding of rectangle and redraw.
        self.set_visible(True)
        self.redraw()
        self.callback_on_release(self.geometry)

    def press(self, event):
        if event.button != 1 or not self.ax.in_axes(event):
            return
        self._set_active_handle(event)
        if self.active_handle is None:
            # Clear previous rectangle before drawing new rectangle.
            self.set_visible(False)
            self.redraw()
        self.set_visible(True)
        RectangleSelector.press(self, event)

    def _set_active_handle(self, event):
        """Set active handle based on the location of the mouse event"""
        # Note: event.xdata/ydata in data coordinates, event.x/y in pixels
        c_idx, c_dist = self._corner_handles.closest(event.x, event.y)
        e_idx, e_dist = self._edge_handles.closest(event.x, event.y)

        # Set active handle as closest handle, if mouse click is close enough.
        if c_dist > self.maxdist and e_dist > self.maxdist:
            self.active_handle = None
            return
        elif c_dist < e_dist:
            self.active_handle = self._corner_order[c_idx]
        else:
            self.active_handle = self._edge_order[e_idx]

        # Save coordinates of rectangle at the start of handle movement.
        x1, x2, y1, y2 = self.extents
        # Switch variables so that only x2 and/or y2 are updated on move.
        if self.active_handle in ["W", "SW", "NW"]:
            x1, x2 = x2, event.xdata
        if self.active_handle in ["N", "NW", "NE"]:
            y1, y2 = y2, event.ydata
        self._extents_on_press = x1, x2, y1, y2

    def onmove(self, event):
        if self.eventpress is None or not self.ax.in_axes(event):
            return

        if self.active_handle is None:
            # New rectangle
            x1 = self.eventpress.xdata
            y1 = self.eventpress.ydata
            x2, y2 = event.xdata, event.ydata
        else:
            x1, x2, y1, y2 = self._extents_on_press
            if self.active_handle in ["E", "W"] + self._corner_order:
                x2 = event.xdata
            if self.active_handle in ["N", "S"] + self._corner_order:
                y2 = event.ydata
        self.extents = (x1, x2, y1, y2)
        self.callback_on_move(self.geometry)

    @property
    def geometry(self):
        return self.extents