class ColorMapperFixSingleVal(ColorMapper): coloratval = ColorTrait('black') val = 0 def map_screen(self, data_array): res = super(ColorMapperFixSingleVal,self).map_screen(data_array) res[data_array==self.val] = self.coloratval_ return res
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()
class Plot_i(HasTraits): plot = Instance(Plot) color = ColorTrait('blue') marker = marker_trait marker_size = Int(4) line_width = Int(4) traits_view = View(Group(Tabbed(Group( \ Group(Item('color', label="Color"), \ Item('marker', label="Marker"), \ orientation = 'vertical'), \ Group( \ Item('marker_size', label= "Size"), \ Item('line_width', label = 'Linewidth'), \ orientation = 'vertical'), \ dock = 'tab', orientation = 'vertical')), \ Item('plot', editor=ComponentEditor(), show_label=False), orientation = 'horizontal'), \ width=800, height=600, resizable=True, title="Chaco Plot") def __init__(self,X,Y): super(Plot_i, self).__init__() self.load_data(X,Y) self.start() def load_data(self,X,Y) : self.X = X self.Y = Y plotdata = ArrayPlotData(x = X, y = Y) plot = Plot(plotdata) self.renderer_line = plot.plot(('x','y'),type = 'line', color = "blue")[0] self.renderer_scat = plot.plot(('x','y'),type = 'scatter', color = "blue")[0] self.plot = plot def start(self): self.configure_traits() def _color_changed(self): self.renderer_line.color = self.color self.renderer_scat.color = self.color def _marker_changed(self): self.renderer_scat.marker = self.marker def _marker_size_changed(self): self.renderer_scat.marker_size = self.marker_size def _line_width_changed(self): self.renderer_line.line_width = self.line_width
class ScatterPlotTraits(HasTraits): plot = Instance(Plot) color = ColorTrait("blue") marker = marker_trait marker_size = Int(4) x = Array() y = Array() traits_view = View(Group(Item('color', label="Color", style="custom"), Item('marker', label="Marker"), Item('marker_size', label="Size"), Item('plot', editor=ComponentEditor(), show_label=False), orientation="vertical"), width=800, height=600, resizable=True, title="Chaco Plot") def __init__(self): super(ScatterPlotTraits, self).__init__() x = linspace(-14, 14, 10) y = sin(x) * x**3 plotdata = ArrayPlotData(x=x, y=y) plot = Plot(plotdata) self.x = x self.y = y self.renderer = plot.plot(("x", "y"), type="scatter", color="blue")[0] self.plot = plot def _color_changed(self): self.renderer.color = self.color def _marker_changed(self): self.renderer.marker = self.marker def _marker_size_changed(self): self.renderer.marker_size = self.marker_size
class Line(AbstractOverlay): orientation = Enum("h", "v") color = ColorTrait("green") width = Int(1) def overlay(self, component, gc, *args, **kw): if component is None: return gc.save_state() gc.set_stroke_color(self.color_) gc.set_line_width(self.width) if self.orientation == "h": mid = component.y + component.height / 2 gc.move_to(component.x, mid) gc.line_to(component.x2, mid) else: mid = component.x + component.width / 2 gc.move_to(mid, component.y) gc.line_to(mid, component.y2) gc.stroke_path() gc.restore_state()
from enthought.template.api \ import MutableTemplate, TRange, TStr, TList, TDerived from enable_editor \ import EnableEditor from scatter_plot \ import ScatterPlot #------------------------------------------------------------------------------- # Trait definitions: #------------------------------------------------------------------------------- # Template color trait: TColor = ColorTrait(template='copy') #------------------------------------------------------------------------------- # 'ScatterPlotNM' class: #------------------------------------------------------------------------------- class ScatterPlotNM(MutableTemplate): #-- Template Traits -------------------------------------------------------- # The title of the plot: title = TStr('NxM Scatter Plots') # The type of marker to use. This is a mapped trait using strings as the # keys:
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
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()
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()
class StackedRenderer(BaseXYPlot): index_name = Str('date') value_names = List(Str) # Accept any recarray as data data = Array index = Property value = Property # Traits for setting appearance. # The line style {"dot", "dash", "solid", others?} line_style = LineStyle # Outline thickness. Use 0 for no outline line_width = Float(1.0) # Outline color line_color = ColorTrait("black") def _gather_points(self): """ Gathers up the data points that are within our bounds and stores them """ if self._cache_valid: return if not self.index: return values = [] idx = self.data[self.index_name].get_data() index_mask = self.index_range.mask_data(idx) for itm in self.data.dtype.names: values.append(self.data[itm][index_mask]) print idx, values value_lengths = array(map(len, values)) if len(idx) == 0 or any(value_lengths == 0) or any( len(idx) != value_lengths): print "Chaco: using empty dataset; index_len=%d, value_lengths=%s." \ % (len(idx), str(value_lengths)) self._cached_data_pts = [] self._cache_valid = True return points = column_stack([ idx, ] + values) for ds in [self.open, self.high, self.low, self.close, self.average]: value, tmp_value_mask = ds.get_data_mask() values.append(value) if value_mask is None: value_mask = tmp_value_mask else: value_mask &= tmp_value_mask # Broaden the range masks by 1 #index_range_mask = broaden(self.index_mapper.range.mask_data(index)) #value_range_mask = broaden(self.value_mapper.range.mask_data(points, high_ndx=2, low_ndx=3)) index_range_mask = self.index_mapper.range.mask_data(idx) value_range_mask = self.value_mapper.range.mask_data(points, high_ndx=2, low_ndx=3) nan_mask = invert(isnan(index_mask)) & invert(isnan(value_mask)) point_mask = index_mask & value_mask & nan_mask & \ index_range_mask & value_range_mask self._cached_data_pts = compress(point_mask, points, axis=0) self._cache_valid = True return def _draw_component(self, gc, view_bounds=None, mode="normal"): # Gather the points within the view range into self._cached_data_pts #self._gather_points() self._gather_points() if self._cached_data_pts.size == 0: # No data. return names = self.data.dtype.names pts = zip(names, self._cached_data_pts.transpose()) for pt in pts: setattr(self, *pt) # Map data points into screen space values = [self.index_mapper.map_screen(getattr(self, self.index_name))] for itm in names: if itm != self.index_name: values.append(self.value_mapper.map_screen(getattr(self, itm))) # Render the screen space points gc.save_state() gc.set_antialias(False) gc.clip_to_rect(self.x, self.y, self.width, self.height) self._render(gc, values) gc.restore_state() def _get_index(self): return self.data[self.index_name] def _set_index(self, new_index): self.index = new_index def _get_value(self): return self.data[self.value_name] def _set_value(self, new_value): self.value = new_value # EOF ####################################################################
class Thingy(HasTraits): color = ColorTrait('black')
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()
class ZoomOverlay(AbstractOverlay): ''' Adapted from a Chaco example. ''' source = Instance(BaseXYPlot) destination = Instance(Component) border_color = ColorTrait((0, 0, 0.7, 1)) border_width = Int(1) fill_color = ColorTrait("lightblue") alpha = Float(0.3) traits_view = View(Group(Item('fill_color', label="Color", style="simple"), Item('border_width', label="Border Width", style="custom"), Item('border_color', label="Border Color"), orientation="vertical"), width=500, height=300, resizable=True, title="Configure Settings", buttons=['OK', 'Cancel']) #*************************************calculate_points()************************************* def calculate_points(self, component): ''' Args: Returns: Raises: ''' # find selection range on source plot x_start, x_end = self._get_selection_screencoords() if x_start > x_end: x_start, x_end = x_end, x_start y_end = self.source.y y_start = self.source.y2 left_top = array([x_start, y_end]) left_mid = array([x_start, y_start]) right_top = array([x_end, y_end]) right_mid = array([x_end, y_start]) # Offset y because we want to avoid overlapping the trapezoid with the topmost # pixels of the destination plot. #y = self.destination.y + 1 y = 100 left_end = array([self.destination.x, y]) right_end = array([self.source.x2, y]) polygon = array( (left_top, left_mid, left_end, right_end, right_mid, right_top)) left_line = array((left_top, left_mid, left_end)) right_line = array((right_end, right_mid, right_top)) return left_line, right_line, polygon #*************************************overlay()************************************* def overlay(self, component, gc, view_bounds=None, mode="normal"): ''' Args: Returns: Raises: ''' tmp = self._get_selection_screencoords() if tmp is None: return left_line, right_line, polygon = self.calculate_points(component) with gc: gc.translate_ctm(*component.position) gc.set_alpha(self.alpha) gc.set_fill_color(self.fill_color_) gc.set_line_width(self.border_width) gc.set_stroke_color(self.border_color_) gc.begin_path() gc.lines(polygon) gc.fill_path() gc.begin_path() gc.lines(left_line) gc.lines(right_line) gc.stroke_path() return #*************************************_get_selection_screencords()************************************* def _get_selection_screencoords(self): ''' Args: Returns: Raises: ''' selection = self.source.index.metadata["selections"] if selection is not None and len(selection) == 2: mapper = self.source.index_mapper return mapper.map_screen(array(selection)) else: return None #*************************************_source_changed()************************************* def _source_changed(self, old, new): ''' Args: Returns: Raises: ''' if old is not None and old.controller is not None: old.controller.on_trait_change(self._selection_update_handler, "selection", remove=True) if new is not None and new.controller is not None: new.controller.on_trait_change(self._selection_update_handler, "selection") return #*************************************_selection_update_handler()************************************* def _selection_update_handler(self, value): ''' Args: Returns: Raises: ''' if value is not None and self.destination is not None: r = self.destination.index_mapper.range start, end = amin(value), amax(value) r.low = start r.high = end self.source.request_redraw() self.destination.request_redraw() return