content: import numpy as np
  from matplotlib import lines
  from ...viewer.canvastools.base import CanvasToolBase, ToolHandles
  
  
  __all__ = ['LineTool', 'ThickLineTool']
  
  
  class LineTool(CanvasToolBase):
      """Widget for line selection in a plot.
  
      Parameters
      ----------
      manager : Viewer or PlotPlugin.
          Skimage viewer or plot plugin 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, manager, on_move=None, on_release=None, on_enter=None,
                   maxdist=10, line_props=None, handle_props=None,
                   **kwargs):
          super(LineTool, self).__init__(manager, 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
          self.manager.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
  
  
  class ThickLineTool(LineTool):
      """Widget for line selection in a plot.
  
      The thickness of the line can be varied using the mouse scroll wheel, or
      with the '+' and '-' keys.
  
      Parameters
      ----------
      manager : Viewer or PlotPlugin.
          Skimage viewer or plot plugin 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.
      on_change : function
          Function called whenever the line thickness is changed.
      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, manager, on_move=None, on_enter=None, on_release=None,
                   on_change=None, maxdist=10, line_props=None, handle_props=None):
          super(ThickLineTool, self).__init__(manager,
                                              on_move=on_move,
                                              on_enter=on_enter,
                                              on_release=on_release,
                                              maxdist=maxdist,
                                              line_props=line_props,
                                              handle_props=handle_props)
  
          if on_change is None:
              def on_change(*args):
                  pass
          self.callback_on_change = on_change
  
      def on_scroll(self, event):
          if not event.inaxes:
              return
          if event.button == 'up':
              self._thicken_scan_line()
          elif event.button == 'down':
              self._shrink_scan_line()
  
      def on_key_press(self, event):
          if event.key == '+':
              self._thicken_scan_line()
          elif event.key == '-':
              self._shrink_scan_line()
  
      def _thicken_scan_line(self):
          self.linewidth += 1
          self.update()
          self.callback_on_change(self.geometry)
  
      def _shrink_scan_line(self):
          if self.linewidth > 1:
              self.linewidth -= 1
              self.update()
              self.callback_on_change(self.geometry)
  
  
  if __name__ == '__main__':  # pragma: no cover
      from ... import data
      from ...viewer import ImageViewer
  
      image = data.camera()
  
      viewer = ImageViewer(image)
      h, w = image.shape
  
      line_tool = ThickLineTool(viewer)
      line_tool.end_points = ([w/3, h/2], [2*w/3, h/2])
      viewer.show()
  