from enthought.enable.api import BaseTool, ColorTrait from enthought.traits.api import Any, Bool, Dict, Enum, HasTraits, Int, List, Trait, Tuple class RangeController(HasTraits): canvas = Any # The list of active plots and which of their ranges was set plots_ranges = List # Stores the old ranges _ranges = Dict def notify(self, axistool, rangename, type, event): plot = axistool.component range = getattr(plot, rangename) if (type == "down") and ((plot, rangename) not in self.plots_ranges): if len(self.plots_ranges) > 0: src_plot, src_rangename = self.plots_ranges[0] src_range = getattr(src_plot, src_rangename) self.link(src_range, plot, rangename) self.plots_ranges.append((plot, rangename)) else: if (plot, rangename) in self.plots_ranges: if len(self.plots_ranges) > 1: self.unlink(plot, rangename) self.plots_ranges.remove((plot, rangename)) return True def link(self, src_range, dst_plot, dst_rangename): self._ranges[(dst_plot, dst_rangename)] = getattr(dst_plot, dst_rangename) setattr(dst_plot, dst_rangename, src_range) dst_plot.request_redraw() def unlink(self, plot, rangename): setattr(plot, rangename, self._ranges.pop((plot, rangename))) plot.request_redraw() class AxisTool(BaseTool): # The object to notify when we've been clicked # We notify by calling its .notify method, which should have the # signature: # should_handle_event = notify(axis_tool, axis, down_or_up, event) # # It should return a bool indicating whether or not we should process the # event. range_controller = Any down_tick_color = ColorTrait("red") down_axis_line_color = ColorTrait("red") down_tick_label_color = ColorTrait("red") down_bgcolor = ColorTrait("lightgray") down_border_visible = Bool(True) down_border_color = Trait(None, None, ColorTrait) _cached_tick_color = ColorTrait _cached_axis_line_color = ColorTrait _cached_tick_labl_color = ColorTrait _cached_bgcolor = ColorTrait _cached_border_visible = Bool(True) _cached_border_color = ColorTrait attr_list = ("tick_color", "axis_line_color", "tick_label_color", "bgcolor", "border_visible", "border_color") def normal_left_down(self, event): if self.component is None: return plot = self.component if plot.index_axis.is_in(event.x, event.y): axis = plot.index_axis rangename = "index_range" elif plot.value_axis.is_in(event.x, event.y): axis = plot.value_axis rangename = "value_range" else: return # If we have a controller, we let it decide whether # or not we get to handle the event. if self.range_controller is not None: should_handle = self.range_controller.notify(self, rangename, "down", event) if not should_handle: return for attr in self.attr_list: cached = "_cached_" + attr down = "down_" + attr setattr(self, cached, getattr(axis, attr)) if getattr(self, down) is not None: setattr(axis, attr, getattr(self, down)) axis.request_redraw() plot._debug = True event.handled = True return def normal_left_up(self, event): if self.component is None: return plot = self.component if plot.index_axis.is_in(event.x, event.y): axis = plot.index_axis rangename = "index_range" elif plot.value_axis.is_in(event.x, event.y): axis = plot.value_axis rangename = "value_range" else: return if self.range_controller is not None: should_handle = self.range_controller.notify(self, rangename, "up", event) if not should_handle: return for attr in self.attr_list: cached = "_cached_" + attr setattr(axis, attr, getattr(self, cached)) axis.request_redraw() event.handled = True return class MPAxisTool(AxisTool): cur_bid = Int(-1) _last_blob_pos = Tuple def normal_blob_down(self, event): if self.cur_bid == -1: self.cur_bid = event.bid if hasattr(event, "bid"): event.window.capture_blob(self, event.bid, event.net_transform()) self.normal_left_down(event) self._last_blob_pos = (event.x, event.y) def normal_blob_up(self, event): print "Axis blob up" if event.bid == self.cur_bid: if hasattr(event, "bid"): event.window.release_blob(event.bid) self.cur_bid = -1 event.x, event.y = self._last_blob_pos self.normal_left_up(event)