示例#1
0
class PolygonSelection(cytoflow.views.ScatterplotView):
    """Plots, and lets the user interact with, a 2D polygon selection.
    
    Attributes
    ----------
    op : Instance(PolygonOp)
        The operation on which this selection view is operating
        
    huefacet : Str
        The conditioning variable to show multiple colors on this plot
        
    subset : Str
        The string for subsetting the plot

    interactive : Bool
        is this view interactive?  Ie, can the user set the polygon verticies
        with mouse clicks?
        
    Notes
    -----
    We inherit `xfacet` and `yfacet` from `cytoflow.views.ScatterPlotView`, but
    they must both be unset!
        
    Examples
    --------

    In an IPython notebook with `%matplotlib notebook`
    
    >>> s = flow.ScatterplotView(xchannel = "V2-A",
    ...                          ychannel = "Y2-A")
    >>> poly = s.default_view()
    >>> poly.plot(ex2)
    >>> poly.interactive = True
    """

    id = Constant('edu.mit.synbio.cytoflow.views.polygon')
    friendly_id = Constant("Polygon Selection")

    op = Instance(IOperation)
    name = DelegatesTo('op')
    xchannel = DelegatesTo('op')
    ychannel = DelegatesTo('op')
    interactive = Bool(False, transient=True)

    # internal state.
    _ax = Any(transient=True)
    _cursor = Instance(Cursor, transient=True)
    _path = Instance(mpl.path.Path, transient=True)
    _patch = Instance(mpl.patches.PathPatch, transient=True)
    _line = Instance(mpl.lines.Line2D, transient=True)
    _drawing = Bool(transient=True)
    _last_draw_time = Float(0.0, transient=True)
    _last_click_time = Float(0.0, transient=True)

    def plot(self, experiment, **kwargs):
        """Plot self.view, and then plot the selection on top of it."""

        if not experiment:
            raise util.CytoflowViewError("No experiment specified")

        if self.xfacet:
            raise util.CytoflowViewError(
                "RangeSelection.xfacet must be empty or `Undefined`")

        if self.yfacet:
            raise util.CytoflowViewError(
                "RangeSelection.yfacet must be empty or `Undefined`")

        super(PolygonSelection, self).plot(experiment, **kwargs)
        self._ax = plt.gca()
        self._draw_poly()
        self._interactive()

    @on_trait_change('op.vertices', post_init=True)
    def _draw_poly(self):
        if not self._ax:
            return

        if self._patch and self._patch in self._ax.patches:
            self._patch.remove()

        if self._drawing or not self.op.vertices or len(self.op.vertices) < 3 \
                         or any([len(x) != 2 for x in self.op.vertices]):
            return

        patch_vert = np.concatenate(
            (np.array(self.op.vertices), np.array((0, 0), ndmin=2)))

        self._patch = \
            mpl.patches.PathPatch(mpl.path.Path(patch_vert, closed = True),
                                  edgecolor="black",
                                  linewidth = 1.5,
                                  fill = False)

        self._ax.add_patch(self._patch)
        plt.draw_if_interactive()

    @on_trait_change('interactive', post_init=True)
    def _interactive(self):
        if self._ax and self.interactive:
            self._cursor = Cursor(self._ax, horizOn=False, vertOn=False)
            self._cursor.connect_event('button_press_event', self._onclick)
            self._cursor.connect_event('motion_notify_event', self._onmove)
        elif self._cursor:
            self._cursor.disconnect_events()
            self._cursor = None

    def _onclick(self, event):
        """Update selection traits"""
        if not self._ax:
            return

        if (self._cursor.ignore(event)):
            return

        # we have to check the wall clock time because the IPython notebook
        # doesn't seem to register double-clicks
        if event.dblclick or (time.clock() - self._last_click_time < 0.5):
            self._drawing = False
            self.op.vertices = map(tuple, self._path.vertices)
            self.op._xscale = plt.gca().get_xscale()
            self.op._yscale = plt.gca().get_yscale()
            self._path = None
            return

        self._last_click_time = time.clock()

        self._drawing = True
        if self._patch and self._patch in self._ax.patches:
            self._patch.remove()

        if self._path:
            vertices = np.concatenate((self._path.vertices,
                                       np.array((event.xdata, event.ydata),
                                                ndmin=2)))
        else:
            vertices = np.array((event.xdata, event.ydata), ndmin=2)

        self._path = mpl.path.Path(vertices, closed=False)
        self._patch = mpl.patches.PathPatch(self._path,
                                            edgecolor="black",
                                            fill=False)

        self._ax.add_patch(self._patch)
        plt.draw_if_interactive()

    def _onmove(self, event):

        if not self._ax:
            return

        if (self._cursor.ignore(event) or not self._drawing or not self._path
                or self._path.vertices.shape[0] == 0 or not event.xdata
                or not event.ydata):
            return

        # only draw 5 times/sec
        if (time.clock() - self._last_draw_time < 0.2):
            return

        self._last_draw_time = time.clock()

        if self._line and self._line in self._ax.lines:
            self._line.remove()

        xdata = [self._path.vertices[-1, 0], event.xdata]
        ydata = [self._path.vertices[-1, 1], event.ydata]
        self._line = mpl.lines.Line2D(xdata, ydata, linewidth=1, color="black")

        self._ax.add_line(self._line)
        plt.gcf().canvas.draw()
示例#2
0
class PolygonSelection(HasStrictTraits):
    """Plots, and lets the user interact with, a 2D selection.
    
    Attributes
    ----------
    polygon : Instance(numpy.ndarray)
        The polygon vertices
        
    view : Instance(IView)
        the IView that this view is wrapping.  I suggest that if it's another
        ISelectionView, that its `interactive` property remain False. >.>
        
    interactive : Bool
        is this view interactive?  Ie, can the user set the polygon verticies
        with mouse clicks?
    """
    
    id = "edu.mit.synbio.cytoflow.views.polygon"
    friendly_id = "Polygon Selection"
    
    view = Instance(IView, transient = True)
    interactive = Bool(False, transient = True)
    
    vertices = List((Float, Float))
    
    # internal state.
    _cursor = Instance(Cursor, transient = True)
    _path = Instance(mpl.path.Path, transient = True)
    _patch = Instance(mpl.patches.PathPatch, transient = True)
    _line = Instance(mpl.lines.Line2D, transient = True)
    _drawing = Bool(transient = True)
        
    def plot(self, experiment, **kwargs):
        """Plot self.view, and then plot the selection on top of it."""
        self.view.plot(experiment, **kwargs)
        self._draw_poly()

    def is_valid(self, experiment):
        """If the decorated view is valid, we are too."""
        return self.view.is_valid(experiment)
    
    @on_trait_change('vertices')
    def _draw_poly(self):
        ca = plt.gca()
         
        if self._patch and self._patch in ca.patches:
            self._patch.remove()
            
        if self._drawing or not self.vertices or len(self.vertices) < 3 \
           or any([len(x) != 2 for x in self.vertices]):
            return
             

        patch_vert = np.concatenate((np.array(self.vertices), 
                                    np.array((0,0), ndmin = 2)))
                                    
        self._patch = \
            mpl.patches.PathPatch(mpl.path.Path(patch_vert, closed = True),
                                  edgecolor="black",
                                  linewidth = 1.5,
                                  fill = False)
            
        ca.add_patch(self._patch)
        plt.gcf().canvas.draw()
    
    @on_trait_change('interactive')
    def _interactive(self):
        if self.interactive:
            ax = plt.gca()
            self._cursor = Cursor(ax, horizOn = False, vertOn = False)            
            self._cursor.connect_event('button_press_event', self._onclick)
            self._cursor.connect_event('motion_notify_event', self._onmove)
        else:
            self._cursor.disconnect_events()
            self._cursor = None       
    
    def _onclick(self, event): 
        """Update selection traits"""      
        if(self._cursor.ignore(event)):
            return
        
        if event.dblclick:
            self._drawing = False
            self.vertices = map(tuple, self._path.vertices)
            self._path = None
            return
                
        ca = plt.gca()
                
        self._drawing = True
        if self._patch and self._patch in ca.patches:
            self._patch.remove()
            
        if self._path:
            vertices = np.concatenate((self._path.vertices,
                                      np.array((event.xdata, event.ydata), ndmin = 2)))
        else:
            vertices = np.array((event.xdata, event.ydata), ndmin = 2)
            
        self._path = mpl.path.Path(vertices, closed = False)
        self._patch = mpl.patches.PathPatch(self._path, 
                                            edgecolor = "black",
                                            fill = False)

        ca.add_patch(self._patch)
        plt.gcf().canvas.draw()
        
    def _onmove(self, event):       
         
        if(self._cursor.ignore(event) 
           or not self._drawing
           or not self._path
           or self._path.vertices.shape[0] == 0
           or not event.xdata
           or not event.ydata):
            return
        
        ca = plt.gca()
         
        if not ca:
            return
         
        if self._line and self._line in ca.lines:
            self._line.remove()
            
        xdata = [self._path.vertices[-1, 0], event.xdata]
        ydata = [self._path.vertices[-1, 1], event.ydata]
        self._line = mpl.lines.Line2D(xdata, ydata, linewidth = 1, color = "black")
        
        ca.add_line(self._line)
        plt.gcf().canvas.draw()
示例#3
0
class PolygonSelection(cytoflow.views.ScatterplotView):
    """Plots, and lets the user interact with, a 2D polygon selection.
    
    Attributes
    ----------
    op : Instance(PolygonOp)
        The operation on which this selection view is operating
        
    huefacet : Str
        The conditioning variable to show multiple colors on this plot
        
    subset : Str
        The string for subsetting the plot

    interactive : Bool
        is this view interactive?  Ie, can the user set the polygon verticies
        with mouse clicks?
        
    Notes
    -----
    We inherit `xfacet` and `yfacet` from `cytoflow.views.ScatterPlotView`, but
    they must both be unset!
        
    Examples
    --------

    In an IPython notebook with `%matplotlib notebook`
    
    >>> s = flow.ScatterplotView(xchannel = "V2-A",
    ...                          ychannel = "Y2-A")
    >>> poly = s.default_view()
    >>> poly.plot(ex2)
    >>> poly.interactive = True
    """
    
    id = Constant('edu.mit.synbio.cytoflow.views.polygon')
    friendly_id = Constant("Polygon Selection")
    
    op = Instance(IOperation)
    name = DelegatesTo('op')
    xchannel = DelegatesTo('op')
    ychannel = DelegatesTo('op')
    interactive = Bool(False, transient = True)

    # internal state.
    _ax = Any(transient = True)
    _cursor = Instance(Cursor, transient = True)
    _path = Instance(mpl.path.Path, transient = True)
    _patch = Instance(mpl.patches.PathPatch, transient = True)
    _line = Instance(mpl.lines.Line2D, transient = True)
    _drawing = Bool(transient = True)
    _last_draw_time = Float(0.0, transient = True)
    _last_click_time = Float(0.0, transient = True)
        
    def plot(self, experiment, **kwargs):
        """Plot self.view, and then plot the selection on top of it."""
        
        if not experiment:
            raise util.CytoflowViewError("No experiment specified")
        
        if self.xfacet:
            raise util.CytoflowViewError("RangeSelection.xfacet must be empty or `Undefined`")
        
        if self.yfacet:
            raise util.CytoflowViewError("RangeSelection.yfacet must be empty or `Undefined`")
        
        super(PolygonSelection, self).plot(experiment, **kwargs)
        self._ax = plt.gca()
        self._draw_poly()
        self._interactive()
    
    @on_trait_change('op.vertices', post_init = True)
    def _draw_poly(self):
        if not self._ax:
            return
         
        if self._patch and self._patch in self._ax.patches:
            self._patch.remove()
            
        if self._drawing or not self.op.vertices or len(self.op.vertices) < 3 \
                         or any([len(x) != 2 for x in self.op.vertices]):
            return
             
        patch_vert = np.concatenate((np.array(self.op.vertices), 
                                    np.array((0,0), ndmin = 2)))
                                    
        self._patch = \
            mpl.patches.PathPatch(mpl.path.Path(patch_vert, closed = True),
                                  edgecolor="black",
                                  linewidth = 1.5,
                                  fill = False)
            
        self._ax.add_patch(self._patch)
        plt.draw_if_interactive()
    
    @on_trait_change('interactive', post_init = True)
    def _interactive(self):
        if self._ax and self.interactive:
            self._cursor = Cursor(self._ax, horizOn = False, vertOn = False)            
            self._cursor.connect_event('button_press_event', self._onclick)
            self._cursor.connect_event('motion_notify_event', self._onmove)
        elif self._cursor:
            self._cursor.disconnect_events()
            self._cursor = None       
    
    def _onclick(self, event): 
        """Update selection traits"""      
        if not self._ax:
            return
        
        if(self._cursor.ignore(event)):
            return
        
        # we have to check the wall clock time because the IPython notebook
        # doesn't seem to register double-clicks
        if event.dblclick or (time.clock() - self._last_click_time < 0.5):
            self._drawing = False
            self.op.vertices = map(tuple, self._path.vertices)
            self.op._xscale = plt.gca().get_xscale()
            self.op._yscale = plt.gca().get_yscale()
            self._path = None
            return
        
        self._last_click_time = time.clock()
                
        self._drawing = True
        if self._patch and self._patch in self._ax.patches:
            self._patch.remove()
            
        if self._path:
            vertices = np.concatenate((self._path.vertices,
                                      np.array((event.xdata, event.ydata), ndmin = 2)))
        else:
            vertices = np.array((event.xdata, event.ydata), ndmin = 2)

        self._path = mpl.path.Path(vertices, closed = False)
        self._patch = mpl.patches.PathPatch(self._path, 
                                            edgecolor = "black",
                                            fill = False)

        self._ax.add_patch(self._patch)
        plt.draw_if_interactive()
        
    def _onmove(self, event):       
         
        if not self._ax:
            return
         
        if(self._cursor.ignore(event) 
           or not self._drawing
           or not self._path
           or self._path.vertices.shape[0] == 0
           or not event.xdata
           or not event.ydata):
            return

        # only draw 5 times/sec
        if(time.clock() - self._last_draw_time < 0.2):
            return
        
        self._last_draw_time = time.clock()
         
        if self._line and self._line in self._ax.lines:
            self._line.remove()
            
        xdata = [self._path.vertices[-1, 0], event.xdata]
        ydata = [self._path.vertices[-1, 1], event.ydata]
        self._line = mpl.lines.Line2D(xdata, ydata, linewidth = 1, color = "black")
        
        self._ax.add_line(self._line)
        plt.gcf().canvas.draw()