示例#1
0
class SmithResistanceGrid(AbstractOverlay):
    # The color of the axis line.
    grid_color = ColorTrait("gray")

    # The line thickness (in pixels) of the axis line.
    grid_weight = Float(1.0)

    # The dash style of the axis line.
    grid_style = LineStyle('dot')

    def overlay(self, component, gc, view_bounds=None, mode='normal'):
        """ Draws this component overlaid on another component.

        Overrides AbstractOverlay.
        """
        if not self.visible:
            return
        self._draw_component(gc, view_bounds, mode, component)
        return

    def _draw_component(self,
                        gc,
                        view_bounds=None,
                        mode='normal',
                        component=None):
        """ Draws the component.

        This method is preserved for backwards compatibility. Overrides
        PlotComponent.
        """
        if not self.visible:
            return

        with gc:
            gc.set_stroke_color(self.grid_color_)
            gc.set_line_width(self.grid_weight)
            gc.set_line_dash(self.grid_style_)

            map_x = self.x_mapper.map_screen
            map_y = self.y_mapper.map_screen
            gc.clip_to_rect(
                map_x(self.x_mapper.range.low), map_y(self.y_mapper.range.low),
                map_x(self.x_mapper.range.high) -
                map_x(self.x_mapper.range.low),
                map_y(self.y_mapper.range.high) -
                map_y(self.y_mapper.range.low))

            x_center = self.x_mapper.map_screen(0)
            y_center = self.y_mapper.map_screen(0)
            radius = self.y_mapper.map_screen(1) - self.y_mapper.map_screen(0)

            # TODO: adapt to zoom level (fixed number of pixels of spacing)
            for i in range(10):
                r = i * radius / 10
                gc.arc(x_center + r, y_center, radius - r, 0, 2 * pi)
                gc.stroke_path()
示例#2
0
class MyLineInspector(DragTool):

    # The axis that this tool is parallel to.
    axis = Enum("index", "value", "index_x", "index_y")

    # The possible inspection modes of the tool.
    #
    # space:
    #    The tool maps from screen space into the data space of the plot.
    # indexed:
    #    The tool maps from screen space to an index into the plot's index array.
    inspect_mode = Enum("space", "indexed")

    # Respond to user mouse events?
    is_interactive = Bool(True)

    # Does the tool respond to updates in the metadata on the data source
    # and update its own position?
    is_listener = Bool(False)

    # If interactive, does the line inspector write the current data space point
    # to the appropriate data source's metadata?
    write_metadata = Bool(False)

    # The name of the metadata field to listen or write to.
    metadata_name = Str("selections")

    callback = Any()
    token = Any()
    handle_size = Float(5)

    #------------------------------------------------------------------------
    # Override default values of inherited traits in BaseTool
    #------------------------------------------------------------------------

    # This tool is visible (overrides BaseTool).
    visible = True
    # This tool is drawn as an overlay (overrides BaseTool).
    draw_mode = "overlay"

    # TODO:STYLE

    # Color of the line.
    color = ColorTrait("grey")
    # Width in pixels of the line.
    line_width = Float(1.0)
    # Dash style of the line.
    line_style = LineStyle("dash")

    index_position = Float(-1)

    # Last recorded position of the mouse
    _last_position = Trait(None, Any)

    def draw(self, gc, view_bounds=None):
        """ Draws this tool on a graphics context.  
        
        Overrides BaseTool.
        """
        # We draw at different points depending on whether or not we are
        # interactive.  If both listener and interactive are true, then the
        # selection metadata on the plot component takes precendence.
        plot = self.component
        if plot is None:
            return

#        if self.is_listener:
#            tmp = self._get_screen_pts()
#        elif self.is_interactive:
#            tmp = self._last_position
#
#        if tmp:
#            sx, sy = tmp
#        else:
#            return

        if self.axis == "index" or self.axis == "index_x":
            if self.index_position == -1:
                self.index_position = int((plot.x + plot.x2) / 2)
                self.index_changed()

            self._draw_vertical_line(gc, self.index_position)
        else:
            if self.index_position == -1:
                self.index_position = int((plot.y + plot.y2) / 2)
                self.index_changed()
            self._draw_horizontal_line(gc, self.index_position)

        return

    def do_layout(self, *args, **kw):
        pass

    def overlay(self, component, gc, view_bounds=None, mode="normal"):
        """ Draws this component overlaid on a graphics context.
        """
        self.draw(gc, view_bounds)
        return

    def is_draggable(self, x, y):
        """ Returns whether the (x,y) position is in a region that is OK to 
        drag.  
        
        Used by the tool to determine when to start a drag.
        """

        if self.axis == "index" or self.axis == "index_x":

            if x > self.index_position - self.handle_size and \
                x < self.index_position + self.handle_size and \
                y > self.component.y2 - 2*self.handle_size and \
                y < self.component.y2:
                return True
        else:

            if y > self.index_position - self.handle_size and \
                y < self.index_position + self.handle_size and \
                x > self.component.x2 - 2*self.handle_size and \
                x < self.component.x2:
                return True

        return False

    def dragging(self, event):
        """ This method is called for every mouse_move event that the tool 
        receives while the user is dragging the mouse.  
        
        It is recommended that subclasses do most of their work in this method.
        """

        if event.x < self.component.x or event.x > self.component.x2 or \
            event.y < self.component.y or event.y > self.component.y2:
            return

        if self.axis == "index" or self.axis == "index_x":
            self.index_position = event.x
        else:
            self.index_position = event.y

        self.index_changed()
#        index = self.component.map_index((event.x,event.y),index_only=True)
#        if self.axis == "index" or self.axis == "index_x":
#            self.index_position = event.x
##            self.component.request_redraw()
#            self.callback(self, self.axis, index[0])
#        else:
#            self.index_position = event.y
##            self.component.request_redraw()
#            self.callback(self, self.axis, index[1])

    def index_changed(self):

        plot = self.component
        if self.axis == "index" or self.axis == "index_x":
            index = plot.map_index((self.index_position, plot.y),
                                   index_only=True)
            value = plot.map_data([(self.index_position, plot.y)])
            self.callback(self, self.axis, index[0], value[0][0])
        else:
            index = plot.map_index((plot.x, self.index_position),
                                   index_only=True)
            value = plot.map_data([(plot.x, self.index_position)])
            self.callback(self, self.axis, index[1], value[0][1])

    def update_index(self, token, axis, index):
        if token == self.token and axis == self.axis:
            plot = self.component

            if self.axis == "index" or self.axis == "index_x":
                dx = plot.index.get_data()[0].get_data()[index]
                dy = plot.index.get_data()[1].get_data()[0]
                sx = plot.map_screen([(dx, dy)])[0][0]
                self.index_position = sx
                self.component.request_redraw()
            else:
                dx = plot.index.get_data()[0].get_data()[0]
                dy = plot.index.get_data()[1].get_data()[index]
                sy = plot.map_screen([(dx, dy)])[0][1]
                self.index_position = sy
                self.component.request_redraw()


#            if self.index_position != index:
#                self.index_position = index
            pass

    def _draw_vertical_line(self, gc, sx):
        """ Draws a vertical line through screen point (sx,sy) having the height
        of the tool's component.
        """

        if sx < self.component.x or sx > self.component.x2:
            return

        gc.save_state()
        try:
            gc.set_stroke_color(self.color_)
            gc.set_line_width(self.line_width)
            gc.set_line_dash(self.line_style_)
            gc.move_to(sx, self.component.y)
            gc.line_to(sx, self.component.y2)

            gc.stroke_path()
            gc.rect(sx - self.handle_size,
                    self.component.y2 - 2 * self.handle_size,
                    2 * self.handle_size, 2 * self.handle_size)
            gc.fill_path()
        finally:
            gc.restore_state()
        return

    def _draw_horizontal_line(self, gc, sy):
        """ Draws a horizontal line through screen point (sx,sy) having the
        width of the tool's component.
        """
        if sy < self.component.y or sy > self.component.y2:
            return

        gc.save_state()
        try:
            gc.set_stroke_color(self.color_)
            gc.set_line_width(self.line_width)
            gc.set_line_dash(self.line_style_)
            gc.move_to(self.component.x, sy)
            gc.line_to(self.component.x2, sy)

            gc.stroke_path()
            gc.rect(self.component.x2 - 2 * self.handle_size,
                    sy - self.handle_size, 2 * self.handle_size,
                    2 * self.handle_size)
            gc.fill_path()
        finally:
            gc.restore_state()
        return
示例#3
0
class SmithCircle(AbstractOverlay):
    # The color of the axis line.
    line_color = ColorTrait("black")

    # The line thickness (in pixels) of the axis line.
    line_weight = Float(1.0)

    # The dash style of the axis line.
    line_style = LineStyle('solid')

    #    def __init__(self, component=None, **kwargs):
    #        # Override init so that our component gets set last.  We want the
    #        # _component_changed() event handler to get run last.
    #        super(self.__class__, self).__init__(**kwargs)
    #        if component is not None:
    #            self.component = component

    def overlay(self, component, gc, view_bounds=None, mode='normal'):
        """ Draws this component overlaid on another component.

        Overrides AbstractOverlay.
        """
        if not self.visible:
            return
        self._draw_component(gc, view_bounds, mode, component)
        return


#    def _draw_overlay(self, gc, view_bounds=None, mode='normal'):
#        """ Draws the overlay layer of a component.

#        Overrides PlotComponent.
#        """
#        self._draw_component(gc, view_bounds, mode)
#        return

    def _draw_component(self,
                        gc,
                        view_bounds=None,
                        mode='normal',
                        component=None):
        """ Draws the component.

        This method is preserved for backwards compatibility. Overrides
        PlotComponent.
        """
        if not self.visible:
            return

        def center_radius(
                mapper):  #data_low, data_high, screen_low, screen_high):
            map = mapper.map_screen
            return map(0), map(1) - map(0)

        with gc:
            gc.set_stroke_color(self.line_color_)
            gc.set_line_width(self.line_weight)
            gc.set_line_dash(self.line_style_)
            map_x = self.x_mapper.map_screen
            map_y = self.y_mapper.map_screen
            gc.clip_to_rect(
                map_x(self.x_mapper.range.low), map_y(self.y_mapper.range.low),
                map_x(self.x_mapper.range.high) -
                map_x(self.x_mapper.range.low),
                map_y(self.y_mapper.range.high) -
                map_y(self.y_mapper.range.low))

            x_center, radius = center_radius(self.x_mapper)
            y_center, radius = center_radius(self.y_mapper)

            # outer ring
            gc.arc(x_center, y_center, radius, 0, 2 * pi)
            gc.stroke_path()

            # horizontal axis
            gc.move_to(x_center - radius, y_center)
            gc.line_to(x_center + radius, y_center)
            gc.stroke_path()
示例#4
0
class SmithReactanceGrid(AbstractOverlay):
    # The color of the axis line.
    grid_color = ColorTrait("gray")

    # The line thickness (in pixels) of the axis line.
    grid_weight = Float(1.0)

    # The dash style of the axis line.
    grid_style = LineStyle('dot')

    def overlay(self, component, gc, view_bounds=None, mode='normal'):
        """ Draws this component overlaid on another component.

        Overrides AbstractOverlay.
        """
        if not self.visible:
            return
        self._draw_component(gc, view_bounds, mode, component)
        return

    def _draw_component(self,
                        gc,
                        view_bounds=None,
                        mode='normal',
                        component=None):
        """ Draws the component.

        This method is preserved for backwards compatibility. Overrides
        PlotComponent.
        """
        if not self.visible:
            return

        with gc:
            gc.set_stroke_color(self.grid_color_)
            gc.set_line_width(self.grid_weight)
            gc.set_line_dash(self.grid_style_)

            map_x = self.x_mapper.map_screen
            map_y = self.y_mapper.map_screen
            gc.clip_to_rect(
                map_x(self.x_mapper.range.low), map_y(self.y_mapper.range.low),
                map_x(self.x_mapper.range.high) -
                map_x(self.x_mapper.range.low),
                map_y(self.y_mapper.range.high) -
                map_y(self.y_mapper.range.low))

            x_center = self.x_mapper.map_screen(0)
            y_center = self.y_mapper.map_screen(0)
            rad = self.y_mapper.map_screen(1) - self.y_mapper.map_screen(0)

            # TODO: adapt to zoom level (fixed number of pixels of spacing)
            rad2 = rad**2
            for i in range(6):
                r1 = i * rad / 5
                tmp = (rad2 - r1**2) / (rad2 + r1**2)
                gc.arc(x_center + rad, y_center + r1, r1, pi - arcsin(tmp),
                       1.5 * pi)
                gc.stroke_path()
                gc.arc(x_center + rad, y_center - r1, r1, 0.5 * pi,
                       pi + arcsin(tmp))
                gc.stroke_path()

            stoprad = 2 * rad / 10
            stoprad2 = stoprad**2
            for i in range(6):
                r2 = 7 * rad / (i + 1)
                tmp = (rad2 - r2**2) / (rad2 + r2**2)
                tmp2 = (stoprad2 - r2**2) / (stoprad2 + r2**2)
                gc.arc(x_center + rad, y_center + r2, r2, pi - arcsin(tmp),
                       pi - arcsin(tmp2))
                gc.stroke_path()
                gc.arc(x_center + rad, y_center - r2, r2, pi + arcsin(tmp2),
                       pi + arcsin(tmp))
                gc.stroke_path()
示例#5
0
class SegmentPlot(BaseXYPlot):
    """ A plot consisting of disconnected line segments.
    """

    # The color of the line.
    color = black_color_trait

    # The color to use to highlight the line when selected.
    selected_color = ColorTrait("lightyellow")

    # The style of the selected line.
    selected_line_style = LineStyle("solid")

    # The name of the key in self.metadata that holds the selection mask
    metadata_name = Str("selections")

    # The thickness of the line.
    line_width = Float(1.0)

    # The line dash style.
    line_style = LineStyle

    # Traits UI View for customizing the plot.
    traits_view = tui.View(tui.Item("color", style="custom"),
                           "line_width",
                           "line_style",
                           buttons=["OK", "Cancel"])

    #------------------------------------------------------------------------
    # Private traits
    #------------------------------------------------------------------------

    # Cached list of non-NaN arrays of (x,y) data-space points; regardless of
    # self.orientation, this is always stored as (index_pt, value_pt).  This is
    # different from the default BaseXYPlot definition.
    _cached_data_pts = List

    # Cached list of non-NaN arrays of (x,y) screen-space points.
    _cached_screen_pts = List

    def hittest(self, screen_pt, threshold=7.0):
        # NotImplemented
        return None

    def get_screen_points(self):
        self._gather_points()
        return [self.map_screen(ary) for ary in self._cached_data_pts]

    #------------------------------------------------------------------------
    # Private methods; implements the BaseXYPlot stub methods
    #------------------------------------------------------------------------

    def _gather_points(self):
        """
        Collects the data points that are within the bounds of the plot and 
        caches them.
        """
        if self._cache_valid or not self.index or not self.value:
            return

        index = self.index.get_data()
        value = self.value.get_data()

        # Check to see if the data is completely outside the view region
        for ds, rng in ((self.index, self.index_range), (self.value,
                                                         self.value_range)):
            low, high = ds.get_bounds()
            if low > rng.high or high < rng.low:
                return

        if len(index) == 0 or len(value) == 0 or len(index) != len(value):
            self._cached_data_pts = []
            self._cache_valid = True

        size_diff = len(value) - len(index)
        if size_diff > 0:
            warnings.warn('len(value) %d - len(index) %d = %d' \
                          % (len(value), len(index), size_diff))
            index_max = len(index)
            value = value[:index_max]
        else:
            index_max = len(value)
            index = index[:index_max]
        if index_max % 2:
            # We need an even number of points. Exclude the final one and
            # continue.
            warnings.warn('need an even number of points; got %d' % index_max)
            index = index[:index_max - 1]
            value = value[:index_max - 1]

        # TODO: restore the functionality of rendering highlighted portions
        # of the line
        #selection = self.index.metadata.get(self.metadata_name, None)
        #if selection is not None and type(selection) in (ndarray, list) and \
        #        len(selection) > 0:

        # Exclude NaNs and Infs.
        finite_mask = np.isfinite(value) & np.isfinite(index)
        # Since the line segment ends are paired, we need to exclude the whole pair if
        # one is not finite.
        finite_mask[::2] &= finite_mask[1::2]
        finite_mask[1::2] &= finite_mask[::2]
        self._cached_data_pts = [
            np.column_stack([index[finite_mask], value[finite_mask]])
        ]
        self._cache_valid = True

    def _render(self, gc, points, selected_points=None):
        if len(points) == 0:
            return

        gc.save_state()
        try:
            gc.set_antialias(True)
            gc.clip_to_rect(self.x, self.y, self.width, self.height)

            if selected_points is not None:
                self._render_segments(gc, selected_points,
                                      self.selected_color_,
                                      self.line_width + 10.0,
                                      self.selected_line_style_)

            # Render using the normal style
            self._render_segments(gc, points, self.color_, self.line_width,
                                  self.line_style_)
        finally:
            gc.restore_state()

    def _render_segments(self, gc, points, color, line_width, line_style):
        gc.set_stroke_color(color)
        gc.set_line_width(line_width)
        gc.set_line_dash(line_style)
        gc.begin_path()
        for ary in points:
            if len(ary) > 0:
                gc.line_set(ary[::2], ary[1::2])
        gc.stroke_path()

    @on_trait_change('color,line_style,line_width')
    def _redraw(self):
        self.invalidate_draw()
        self.request_redraw()