diff --git a/chaco/chaco_plot_editor.py b/chaco/chaco_plot_editor.py deleted file mode 100644 index 43f3c21bf..000000000 --- a/chaco/chaco_plot_editor.py +++ /dev/null @@ -1,457 +0,0 @@ -# Enthought library imports -from traits.etsconfig.api import ETSConfig -from enable.api import ( - black_color_trait, - LineStyle, - ColorTrait, - white_color_trait, - MarkerTrait, - Window, -) -from enable.trait_defs.ui.api import RGBAColorEditor -from kiva.trait_defs.kiva_font_trait import KivaFont -from traits.api import Enum, Str, Range, Trait, Tuple, Bool, Int, Any, Property -from traitsui.api import EditorFactory, Item - -# Toolkit dependent imports -from traitsui.toolkit import toolkit_object - -Editor = toolkit_object("editor:Editor") - -# Local relative imports -from .axis import PlotAxis -from .plot_containers import OverlayPlotContainer -from .plot_factory import ( - create_line_plot, - create_scatter_plot, - add_default_grids, - add_default_axes, -) -from .plot_label import PlotLabel - -# Somewhat unorthodox... -from .tools.api import PanTool, ZoomTool - -# ------------------------------------------------------------------------------- -# Trait definitions: -# ------------------------------------------------------------------------------- - -#: Range of values for an axis. -AxisRange = Tuple((0.0, 1.0, 0.01), labels=["Low", "High", "Step"], cols=3) - -#: Range of axis bounds. -AxisBounds = Tuple((0.0, 1.0), labels=["Min", "Max"], cols=2) - -#: Range for the height and width for the plot widget. -PlotSize = Range(50, 1000, 180) - -#: Range of plot line weights. -LineWeight = Range(1, 9, 3) - -#: The color editor to use for various color traits. -color_editor = RGBAColorEditor() - - -USE_DATA_UPDATE = 1 - - -class ChacoPlotItem(Item): - """A TraitsUI Item for a Chaco plot, for use in TraitsUI Views. - - NOTE: ComponentEditor is preferred over this class, as it is more flexible. - """ - - #: Name of the trait that references the index data source. - index = Str - #: Name of the trait that references the value data source. - value = Str - #: Title of the plot (overlaid on the plot container). - title = Str("Plot Editor") - - #: Bounds of the x-axis, used if **x_auto** is False. - x_bounds = AxisBounds - #: Set the x-axis bounds automatically? - x_auto = Bool(True) - #: Bounds of the y-axis, used if **y_auto** is False. - y_bounds = AxisBounds - #: Set the y-axis bounds automatically? - y_auto = Bool(True) - - #: The orientation of the index axis. - orientation = Enum("h", "v") - - # If these are None, then the index/value trait names are used - - #: Label of the x-axis; if None, the **index** name is used. - x_label = Trait(None, None, Str) - #: Name of the trait on the object containing the label of the x-axis. - #: This takes precedence over **x_label**. - x_label_trait = Trait(None, None, Str) - #: Font for the label of the x-axis. - x_label_font = KivaFont("modern 10") - #: Color of the label of the x-axis. - x_label_color = black_color_trait - #: Label of the y-axis; if None, the **value** name is used. - y_label = Trait(None, None, Str) - #: Name of the trait on the object containing the label of the y-axis. - #: This takes precedence over **y_label**. - y_label_trait = Trait(None, None, Str) - #: Font for the label of the y-axis. - y_label_font = KivaFont("modern 10") - #: Color of the label of the y-axis. - y_label_color = black_color_trait - - # General plot properties - - #: Foreground olor of the plot. - color = ColorTrait("blue") - #: Background color of the plot. - bgcolor = white_color_trait - #: Background color of the plot (deprecated). - bg_color = Property # backwards compatibility; deprecated - #: Color of the background padding. - padding_bg_color = ColorTrait("sys_window") - - # Border properties - - #: Width of the plot border - border_width = Int(1) - #: Is the border visible? - border_visible = Bool(False) - #: Line style of the border. - border_dash = LineStyle - #: Color of the border. - border_color = black_color_trait - - #: The type of the plot. - type = Enum("line", "scatter") - #: The type of the plot as a string. - type_trait = Str - - # plot-specific properties. These might not apply to all plot types. - - #: Type of marker (for plots that use markers). - marker = MarkerTrait - #: Size of marker (for plots that use markers). - marker_size = Int(4) - #: Marker outline color (for plots that user markers). - outline_color = black_color_trait - - def __init__(self, index, value, type="line", **traits): - self.index = index - self.value = value - self.type = type - self.name = index - super(ChacoPlotItem, self).__init__(**traits) - - self.editor = ChacoEditorFactory() - - self.editor.plotitem = self - - def _set_bg_color(self, val): - self.bgcolor = val - - def _get_bg_color(self): - return self.bgcolor - - -class ChacoEditorFactory(EditorFactory): - """Editor factory for plot editors.""" - - # --------------------------------------------------------------------------- - # Trait definitions: - # --------------------------------------------------------------------------- - - # Width of the plot editor. - width = PlotSize - # Height of the plot editor. - height = PlotSize - # The ChacoPlotItem associated with this factory. - plotitem = Any - - # --------------------------------------------------------------------------- - # 'Editor' factory methods: - # --------------------------------------------------------------------------- - - def simple_editor(self, ui, object, name, description, parent): - return ChacoPlotEditor( - parent, - factory=self, - ui=ui, - object=object, - name=name, - description=description, - ) - - def text_editor(self, ui, object, name, description, parent): - return ChacoPlotEditor( - parent, - factory=self, - ui=ui, - object=object, - name=name, - description=description, - ) - - def readonly_editor(self, ui, object, name, description, parent): - return ChacoPlotEditor( - parent, - factory=self, - ui=ui, - object=object, - name=name, - description=description, - ) - - -class ChacoPlotEditor(Editor): - """TraitsUI editor for displaying trait values in a Chaco plot.""" - - # --------------------------------------------------------------------------- - # Finishes initializing the editor by creating the underlying toolkit - # widget: - # --------------------------------------------------------------------------- - - def init(self, parent): - """Finishes initializing the editor by creating the underlying toolkit - widget. - """ - factory = self.factory - plotitem = factory.plotitem - - container = OverlayPlotContainer( - padding=50, - fill_padding=True, - bgcolor=plotitem.padding_bg_color, - use_backbuffer=True, - ) - - if plotitem.title != "": - container.overlays.append( - PlotLabel( - plotitem.title, component=container, overlay_position="top" - ) - ) - - self._container = container - window = Window(parent, component=container) - - # FIXME: Toolkit specifc code here. The AbstractWindow should have a - # 'set size' method as part of its API. - self.control = control = window.control - if ETSConfig.toolkit == "wx": - control.SetSize((factory.width, factory.height)) - elif ETSConfig.toolkit == "qt4": - control.resize(factory.width, factory.height) - else: - raise NotImplementedError - - # Attach listeners to the object's traits appropriately so we can - # update the plot when they change. For the _update_axis_grids() - # callback, we have to wrap it in a lambda to keep traits from - # inferring the calling convention based on introspecting the argument - # list. - object = self.object - if USE_DATA_UPDATE == 1: - for name in (plotitem.index, plotitem.value): - object.observe(self._update_data, name) - for name in (plotitem.x_label_trait, plotitem.y_label_trait): - if name and getattr(object, name, None): - object.observe(lambda s: self._update_axis_grids(), name) - if plotitem.type_trait not in ("", None): - object.observe(self.update_editor, plotitem.type_trait) - - # --------------------------------------------------------------------------- - # Disposes of the contents of an editor: - # --------------------------------------------------------------------------- - - def dispose(self): - """Disposes of the contents of the editor.""" - object = self.object - plotitem = self.factory.plotitem - - if USE_DATA_UPDATE == 1: - for name in (plotitem.index, plotitem.value): - object.observe(self._update_data, name, remove=True) - for name in (plotitem.type_trait,): - object.observe(self.update_editor, name, remove=True) - self._destroy_plot() - super(ChacoPlotEditor, self).dispose() - - def _destroy_plot(self): - if self._container and self._plot: - plot = self._plot - del plot.index._data - del plot.index._cached_mask - del plot.value._data - del plot.value._cached_mask - self._container.remove(plot) - self._plot = None - plot.index = None - plot.value = None - - # --------------------------------------------------------------------------- - # Updates the editor when the object trait changes externally to the editor: - # --------------------------------------------------------------------------- - - def update_editor(self, event=None): - """Updates the editor when the object trait changes externally to the - editor. - """ - - factory = self.factory - if factory is None: - return - plotitem = factory.plotitem - - # Remove the old plot - if self._plot is not None: - self._destroy_plot() - - try: - x_values = getattr(self.object, plotitem.index) - y_values = getattr(self.object, plotitem.value) - except: - self._container.request_redraw() - return - - if plotitem.type_trait != "": - plot_type = getattr(self.object, plotitem.type_trait) - else: - plot_type = plotitem.type - - if plotitem.x_auto == True: - index_bounds = None - else: - index_bounds = plotitem.x_bounds - - if plotitem.y_auto == True: - value_bounds = None - else: - value_bounds = plotitem.y_bounds - - # Class-level attribute mapping different plot_type strings to methods for - # creating different types of plots - plot_creator_map = { - "line": self._create_line_plot, - "scatter": self._create_scatter_plot, - } - - if plot_type in plot_creator_map: - plot = plot_creator_map[plot_type]( - plotitem, - (x_values, y_values), - index_bounds=index_bounds, - value_bounds=value_bounds, - orientation=plotitem.orientation, - ) - else: - raise RuntimeError( - "Unknown plot type '%s' in ChacoPlotEditor." % plot_type - ) - - self._set_basic_properties(plot, plotitem) - - self._add_axis_grids(plot, plotitem) - - self._plot = plot - self._container.add(plot) - self._container.request_redraw() - - def _update_data(self, event): - """Updates the editor when the object trait changes externally to the - editor. - """ - if self._plot is None: - self.update_editor() - else: - x_values = getattr(self.object, self.factory.plotitem.index) - y_values = getattr(self.object, self.factory.plotitem.value) - self._plot.index.set_data(x_values) - self._plot.value.set_data(y_values) - - def _set_basic_properties(self, plot, plotitem): - for attr in ( - "color", - "bgcolor", - "border_visible", - "border_width", - "border_dash", - "border_color", - ): - setattr(plot, attr, getattr(plotitem, attr)) - - def _create_line_plot(self, plotitem, values, **kwargs): - plot = create_line_plot(values, **kwargs) - return plot - - def _create_scatter_plot(self, plotitem, values, **kwargs): - plot = create_scatter_plot(values, **kwargs) - for attr in ("marker", "marker_size", "outline_color"): - setattr(plot, attr, getattr(plotitem, attr)) - return plot - - def _add_axis_grids(self, new_plot, plotitem): - value_axis, index_axis = add_default_axes( - new_plot, orientation=plotitem.orientation - ) - add_default_grids(new_plot) - new_plot.tools.append(PanTool(new_plot)) - zoom = ZoomTool(component=new_plot, tool_mode="box", always_on=False) - new_plot.overlays.append(zoom) - - # Update the titles and labels - self._update_axis_grids(new_plot, plotitem) - - def _update_axis_grids(self, plot=None, plotitem=None): - if self.factory is None: - return - - if plot is None: - if self._plot is None: - return - else: - plot = self._plot - if plotitem is None: - plotitem = self.factory.plotitem - - if plotitem.x_label_trait is not None: - htitle = getattr(self.object, plotitem.x_label_trait) - elif plotitem.x_label is not None: - htitle = plotitem.x_label - else: - htitle = plotitem.index - - if plotitem.y_label_trait is not None: - vtitle = getattr(self.object, plotitem.y_label_trait) - elif plotitem.y_label is not None: - vtitle = plotitem.y_label - else: - vtitle = plotitem.value - - if plotitem.orientation == "v": - htitle, vtitle = vtitle, htitle - plot.x_axis.title = htitle - plot.y_axis.title = vtitle - - # This is sort of crappy.. since we are using BaseXYPlots and not - # Plot/DataViews, we actually can't easily get references to the plot's - # index and value axes. So we have to search through the underlays for - # PlotAxis instances whose ranges match the index and value ranges. - for axis in plot.underlays + plot.overlays: - if ( - isinstance(axis, PlotAxis) - and axis.mapper.range is plot.index_range - ): - axis.title_font = plotitem.x_label_font - axis.title_color = plotitem.x_label_color - - for axis in plot.underlays + plot.overlays: - if ( - isinstance(axis, PlotAxis) - and axis.mapper.range is plot.value_range - ): - axis.title_font = plotitem.y_label_font - axis.title_color = plotitem.y_label_color - - plot.request_redraw() diff --git a/docs/source/user_manual/annotated_examples.rst b/docs/source/user_manual/annotated_examples.rst index 5f3b79664..d46c40b17 100644 --- a/docs/source/user_manual/annotated_examples.rst +++ b/docs/source/user_manual/annotated_examples.rst @@ -450,17 +450,6 @@ source: `tabbed_plots.py `_ - -.. image:: example_images/traits_editor.png - ``zoomable_colorbar.py`` ------------------------ Draws a colormapped scatterplot of some random data. diff --git a/examples/demo/advanced/data_stream.py b/examples/demo/advanced/data_stream.py index 111e704fb..66c4f5881 100644 --- a/examples/demo/advanced/data_stream.py +++ b/examples/demo/advanced/data_stream.py @@ -15,6 +15,7 @@ import numpy as np # Enthought imports +from enable.api import ComponentEditor from traits.api import ( Array, Callable, @@ -23,13 +24,14 @@ HasTraits, Instance, Int, + observe, Trait, ) -from traitsui.api import Group, HGroup, Item, View, spring, Handler +from traitsui.api import Group, HGroup, Item, UItem, View, spring, Handler from pyface.timer.api import Timer # Chaco imports -from chaco.chaco_plot_editor import ChacoPlotItem +from chaco.api import ArrayPlotData, Plot class Viewer(HasTraits): @@ -38,30 +40,44 @@ class Viewer(HasTraits): Chaco plot. """ - index = Array + index = Array() - data = Array + data = Array() plot_type = Enum("line", "scatter") - view = View( - ChacoPlotItem( - "index", - "data", - type_trait="plot_type", - resizable=True, - x_label="Time", - y_label="Signal", - color="blue", - bgcolor="white", - border_visible=True, - border_width=1, - padding_bg_color="lightgray", - width=800, - height=380, - marker_size=2, - show_label=False, - ), + plot = Instance(Plot) + + def _plot_default(self): + plot = Plot(ArrayPlotData(x=self.index, y=self.data)) + plot.x_axis.title = "Time" + plot.y_axis.title = "Signal" + + plot.plot( + ("x", "y"), type=self.plot_type, name=self.plot_type, color="blue" + ) + + return plot + + @observe("index,data") + def _update_plot_data(self, event): + if event.name == "index": + self.plot.data.set_data("x", self.index) + else: + self.plot.data.set_data("y", self.data) + + @observe("plot_type") + def _update_plot_type(self, event): + old_plot_type, new_plot_type = event.old, event.new + + self.plot.delplot(old_plot_type) + self.plot.plot( + ("x", "y"), type=new_plot_type, name=new_plot_type, color="blue" + ) + self.plot.invalidate_and_redraw() + + traits_view = View( + UItem("plot", editor=ComponentEditor()), HGroup(spring, Item("plot_type", style="custom"), spring), resizable=True, buttons=["OK"], @@ -117,7 +133,7 @@ def timer_tick(self, *args): # grab the existing data, truncate it, and append the new point. # This isn't the most efficient thing in the world but it works. cur_data = self.viewer.data - new_data = np.hstack((cur_data[-self.max_num_points + 1 :], [new_val])) + new_data = np.hstack((cur_data[-self.max_num_points + 1:], [new_val])) new_index = np.arange( self.num_ticks - len(new_data) + 1, self.num_ticks + 0.01 ) diff --git a/examples/demo/basic/traits_editor.py b/examples/demo/basic/traits_editor.py deleted file mode 100644 index 5b650e34b..000000000 --- a/examples/demo/basic/traits_editor.py +++ /dev/null @@ -1,88 +0,0 @@ -""" 1D Function plotter. - -This example creates a simple 1D function examiner, illustrating the use of -ChacoPlotEditors for displaying simple plot relations, as well as TraitsUI -integration. Any 1D numpy/scipy.special function should work in the function -text box. - - Left-drag pans the plot. - - Mousewheel up and down zooms the plot in and out. - - Pressing "z" brings up the Zoom Box, and you can click-drag a rectangular - region to zoom. If you use a sequence of zoom boxes, pressing alt-left-arrow - and alt-right-arrow moves you forwards and backwards through the "zoom - history". -""" - -# Major library imports -from numpy import linspace, pi - -# Enthought library imports -from traits.api import Array, Dict, Enum, HasTraits, Str -from traitsui.api import Item, View - -# Chaco imports -from chaco.chaco_plot_editor import ChacoPlotEditor, ChacoPlotItem - - -class Foo(HasTraits): - - # Public Traits - xdata = Array - plot_type = Enum("scatter", "line") - eq = Str("sin(x)") - - # Default TraitsUI view - traits_view = View( - ChacoPlotItem( - "xdata", - "_ydata", - type_trait="plot_type", - # Basic axis and label properties - show_label=False, - resizable=True, - orientation="h", - x_label="Index data", - y_label="Value data", - # Plot properties - color="green", - bgcolor="white", - # Specific to scatter plot - marker="circle", - marker_size=2, - outline_color="none", - # Border, padding properties - border_visible=True, - border_width=1, - padding_bg_color="lightgray", - ), - Item("plot_type"), - Item("eq"), - resizable=True, - width=500, - height=500, - ) - - # Private Traits - _d = Dict - _ydata = Array - - def __init__(self, **kwtraits): - super(Foo, self).__init__(**kwtraits) - self._d = dict(x=self.xdata) - exec("from scipy import *", self._d) - exec("from scipy.special import *", self._d) - self._ydata = eval(self.eq, self._d) - - def _eq_changed(self, old, new): - try: - self._ydata = eval(new, self._d) - except: - pass - - -# =============================================================================== -# # demo object that is used by the demo.py application. -# =============================================================================== -demo = Foo(xdata=linspace(-2 * pi, 2 * pi, 100), eq="sin(x)") - -if __name__ == "__main__": - demo.edit_traits(kind="modal") diff --git a/tox.ini b/tox.ini index fb0334dde..870413753 100644 --- a/tox.ini +++ b/tox.ini @@ -155,7 +155,6 @@ exclude = examples/demo/financial_plot_dates.py examples/demo/advanced/asynchronous_updates.py examples/demo/advanced/spec_waterfall.py - examples/demo/advanced/data_stream.py examples/demo/advanced/data_cube.py examples/demo/advanced/scalar_image_function_inspector.py examples/demo/advanced/javascript_hover_tools.py @@ -168,7 +167,6 @@ exclude = examples/demo/financial/stock_prices.py examples/demo/basic/image_inspector.py examples/demo/basic/draw_layers.py - examples/demo/basic/traits_editor.py examples/demo/basic/regression.py examples/demo/basic/scatter_variable_size.py examples/demo/basic/log_plot.py