From 444927446b4fe0be70272a4e1ce27793101d6e12 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Fri, 14 Oct 2022 17:32:26 +0100 Subject: [PATCH 1/8] Add MappedUnion and Optional traits and use Optional where appropriate. --- chaco/base_candle_plot.py | 9 +-- chaco/base_contour_plot.py | 3 +- chaco/chaco_traits.py | 56 ++++++++++++++++++- chaco/data_view.py | 9 +-- chaco/grid.py | 7 ++- chaco/overlays/scatter_inspector_overlay.py | 21 +++---- chaco/plot_containers.py | 3 +- chaco/plots/multi_line_plot.py | 3 +- chaco/plotscrollbar.py | 4 +- chaco/tools/better_selecting_zoom.py | 7 ++- chaco/tools/image_inspector_tool.py | 3 +- chaco/tools/line_segment_tool.py | 5 +- chaco/tools/range_selection.py | 3 +- chaco/transform_color_mapper.py | 7 ++- examples/demo/canvas/axis_tool.py | 3 +- examples/demo/canvas/mptools.py | 7 ++- examples/demo/canvas/plot_clone_tool.py | 3 +- .../demo/canvas/transient_plot_overlay.py | 3 +- 18 files changed, 114 insertions(+), 42 deletions(-) diff --git a/chaco/base_candle_plot.py b/chaco/base_candle_plot.py index e8cca679b..25f7a80d0 100644 --- a/chaco/base_candle_plot.py +++ b/chaco/base_candle_plot.py @@ -17,6 +17,7 @@ # Chaco imports from .base_xy_plot import BaseXYPlot +from .chaco_traits import Optional # TODO: allow to set the width of the bar @@ -54,11 +55,11 @@ class BaseCandlePlot(BaseXYPlot): #: The color of the stems reaching from the bar ends to the min and max #: values. Also the color of the endcap line segments at min and max. If #: None, this defaults to **bar_line_color**. - stem_color = Union(None, ColorTrait("black")) + stem_color = Optional(ColorTrait("black")) #: The color of the line drawn across the bar at the center values. #: If None, this defaults to **bar_line_color**. - center_color = Union(None, ColorTrait("black")) + center_color = Optional(ColorTrait("black")) #: The color of the outline to draw around the bar. outline_color = ColorTrait("black") @@ -69,11 +70,11 @@ class BaseCandlePlot(BaseXYPlot): #: The thickness, in pixels, of the stem lines. If None, this defaults #: to **line_width**. - stem_width = Union(None, Int(1)) + stem_width = Optional(Int(1)) #: The thickeness, in pixels, of the line drawn across the bar at the #: center values. If None, this defaults to **line_width**. - center_width = Union(None, Int(1)) + center_width = Optional(Int(1)) #: Whether or not to draw bars at the min and max extents of the error bar end_cap = Bool(True) diff --git a/chaco/base_contour_plot.py b/chaco/base_contour_plot.py index 9f2adf429..9616a4920 100644 --- a/chaco/base_contour_plot.py +++ b/chaco/base_contour_plot.py @@ -27,6 +27,7 @@ # Local relative imports from .base_2d_plot import Base2DPlot +from .chaco_traits import Optional from .color_mapper import ColorMapper @@ -52,7 +53,7 @@ class BaseContourPlot(Base2DPlot): #: colors is shorter than the number of levels, the values are repeated #: from the beginning of the list. Default is black. #: Colors are associated with levels of increasing value. - colors = Union(None, Str, Instance(ColorMapper), List, Tuple) + colors = Optional(Str, Instance(ColorMapper), List, Tuple) #: If present, the color mapper for the colorbar to look at. color_mapper = Property(Instance(ColorMapper)) diff --git a/chaco/chaco_traits.py b/chaco/chaco_traits.py index f3cdc1a2a..9416bb0e6 100644 --- a/chaco/chaco_traits.py +++ b/chaco/chaco_traits.py @@ -12,7 +12,7 @@ """ # Enthought library imports -from traits.api import Enum +from traits.api import Enum, Union, TraitError # ---------------------------------------------------------------------------- # Box positioning traits: used to specify positions of boxes relative to @@ -24,3 +24,57 @@ #: Values correspond to: top, bottom, left, right, top left, top right, bottom #: left, bottom right box_position_enum = Enum("T", "B", "L", "R", "TL", "TR", "BL", "BR") + + +class MappedUnion(Union): + """Version of the Union trait that handles mapped traits correctly.""" + + #: This is not mapped by default. + is_mapped = False + + def __init__(self, *traits, **metadata): + super().__init__(*traits, **metadata) + + # look for post_setattr and is_mapped on traits + post_setattrs = [] + mapped_traits = [] + for trait in traits: + post_setattr = getattr(trait, "post_setattr", None) + if post_setattr is not None: + post_setattrs.append(post_setattr) + if trait.is_mapped: + self.is_mapped = True + mapped_traits.append(trait) + + if post_setattrs: + self.post_setattrs = post_setattrs + self.post_setattr = self._post_setattr + if self.is_mapped: + self.mapped_traits = mapped_traits + + def mapped_value(self, value): + for trait in self.mapped_traits: + try: + return trait.mapped_value(value) + except Exception: + pass + + return value + + def _post_setattr(self, object, name, value): + for post_setattr in self.post_setattrs: + try: + post_setattr(object, name, value) + return + except TraitError: + pass + + # I am not sure about this + setattr(object, name + "_", value) + + +class Optional(MappedUnion): + """Convenience class""" + + def __init__(self, trait, **metadata): + super().__init__(None, trait, **metadata) diff --git a/chaco/data_view.py b/chaco/data_view.py index 20825b0fa..6e68af389 100644 --- a/chaco/data_view.py +++ b/chaco/data_view.py @@ -20,6 +20,7 @@ from .axis import PlotAxis from .base_1d_mapper import Base1DMapper from .base_2d_plot import Base2DPlot +from .chaco_traits import Optional from .data_range_2d import DataRange2D from .grid import PlotGrid from .linear_mapper import LinearMapper @@ -236,10 +237,10 @@ class DataView(OverlayPlotContainer): observe='y_axis.[title,orientation], x_axis.[title,orientation]' ) - _padding_top = Union(None, Int()) - _padding_bottom = Union(None, Int()) - _padding_left = Union(None, Int()) - _padding_right = Union(None, Int()) + _padding_top = Optional(Int()) + _padding_bottom = Optional(Int()) + _padding_left = Optional(Int()) + _padding_right = Optional(Int()) def _find_padding(self, side): SIDE_TO_TRAIT_MAP = { diff --git a/chaco/grid.py b/chaco/grid.py index b8a23463b..342e765fd 100644 --- a/chaco/grid.py +++ b/chaco/grid.py @@ -46,6 +46,7 @@ # Local, relative imports from .abstract_overlay import AbstractOverlay from .abstract_mapper import AbstractMapper +from .chaco_traits import Optional from .log_mapper import LogMapper from .ticks import AbstractTickGenerator, DefaultTickGenerator @@ -110,11 +111,11 @@ class PlotGrid(AbstractOverlay): #: The dataspace value at which to start this grid. If None, then #: uses the mapper.range.low. - data_min = Union(None, Float) + data_min = Optional(Float) #: The dataspace value at which to end this grid. If None, then uses #: the mapper.range.high. - data_max = Union(None, Float) + data_max = Optional(Float) #: A callable that implements the AbstractTickGenerator Interface. tick_generator = Instance(AbstractTickGenerator) @@ -143,7 +144,7 @@ class PlotGrid(AbstractOverlay): #: Callable : Function that takes an array of dataspace grid ticks #: and returns either an array of shape (N,2) of (starts,ends) #: for each grid point or a single tuple (low, high) - transverse_bounds = Union(None, Tuple, Callable) + transverse_bounds = Optional(Tuple, Callable) #: Mapper in the direction corresponding to self.orientation, i.e. transverse #: to the direction of self.mapper. This is used to compute the screen diff --git a/chaco/overlays/scatter_inspector_overlay.py b/chaco/overlays/scatter_inspector_overlay.py index 8ef88ab9b..c7969df2a 100644 --- a/chaco/overlays/scatter_inspector_overlay.py +++ b/chaco/overlays/scatter_inspector_overlay.py @@ -18,6 +18,7 @@ # Local, relative imports from chaco.abstract_overlay import AbstractOverlay +from chaco.chaco_traits import Optional from chaco.plots.scatterplot import render_markers @@ -32,19 +33,19 @@ class ScatterInspectorOverlay(AbstractOverlay): #: The style to use when a point is hovered over hover_metadata_name = Str("hover") - hover_marker = Union(None, MarkerTrait) - hover_marker_size = Union(None, Int) - hover_line_width = Union(None, Float) - hover_color = Union(None, ColorTrait) - hover_outline_color = Union(None, ColorTrait) + hover_marker = Optional(MarkerTrait) + hover_marker_size = Optional(Int) + hover_line_width = Optional(Float) + hover_color = Optional(ColorTrait) + hover_outline_color = Optional(ColorTrait) #: The style to use when a point has been selected by a click selection_metadata_name = Str("selections") - selection_marker = Union(None, MarkerTrait) - selection_marker_size = Union(None, Int) - selection_line_width = Union(None, Float) - selection_color = Union(None, ColorTrait) - selection_outline_color = Union(None, ColorTrait) + selection_marker = Optional(MarkerTrait) + selection_marker_size = Optional(Int) + selection_line_width = Optional(Float) + selection_color = Optional(ColorTrait) + selection_outline_color = Optional(ColorTrait) # For now, implement the equivalent of this Traits 3 feature manually # using a series of trait change handlers (defined at the end of the diff --git a/chaco/plot_containers.py b/chaco/plot_containers.py index 7ee0046e6..5662b2e6c 100644 --- a/chaco/plot_containers.py +++ b/chaco/plot_containers.py @@ -47,6 +47,7 @@ # Local relative imports from .base_plot_container import BasePlotContainer +from .chaco_traits import Optional __all__ = [ @@ -156,7 +157,7 @@ class GridPlotContainer(BasePlotContainer): #: The amount of space to put on either side of each component, expressed #: as a tuple (h_spacing, v_spacing). - spacing = Union(None, Tuple, List, Array) + spacing = Optional(Tuple, List, Array) #: The vertical alignment of objects that don't span the full height. valign = Enum("bottom", "top", "center") diff --git a/chaco/plots/multi_line_plot.py b/chaco/plots/multi_line_plot.py index 0a98557b4..ef716d932 100644 --- a/chaco/plots/multi_line_plot.py +++ b/chaco/plots/multi_line_plot.py @@ -39,6 +39,7 @@ from chaco.array_data_source import ArrayDataSource from chaco.base import arg_find_runs, bin_search from chaco.base_xy_plot import BaseXYPlot +from chaco.chaco_traits import Optional class MultiLinePlot(BaseXYPlot): @@ -118,7 +119,7 @@ class MultiLinePlot(BaseXYPlot): color = black_color_trait(requires_redraw=True) #: A function that returns the color of lines. Overrides `color` if not None. - color_func = Union(None, Callable) + color_func = Optional(Callable) #: The color to use to highlight the line when selected. selected_color = ColorTrait("lightyellow") diff --git a/chaco/plotscrollbar.py b/chaco/plotscrollbar.py index 063cd8c79..c25f2986f 100644 --- a/chaco/plotscrollbar.py +++ b/chaco/plotscrollbar.py @@ -12,6 +12,8 @@ from enable.api import NativeScrollBar +from .chaco_traits import Optional + class PlotScrollBar(NativeScrollBar): """ @@ -43,7 +45,7 @@ class PlotScrollBar(NativeScrollBar): _mapper = Any() # Stores the index (0 or 1) corresponding to self.axis - _axis_index = Union(None, Int) + _axis_index = Optional(Int) # ---------------------------------------------------------------------- # Public methods diff --git a/chaco/tools/better_selecting_zoom.py b/chaco/tools/better_selecting_zoom.py index 9c4f81fe6..8e2489d03 100644 --- a/chaco/tools/better_selecting_zoom.py +++ b/chaco/tools/better_selecting_zoom.py @@ -10,7 +10,6 @@ import numpy -from chaco.abstract_overlay import AbstractOverlay from enable.api import ColorTrait, KeySpec from traits.api import ( Bool, @@ -22,6 +21,8 @@ Union ) +from chaco.abstract_overlay import AbstractOverlay +from chaco.chaco_traits import Optional from .better_zoom import BetterZoom from .tool_states import SelectedZoomState @@ -91,10 +92,10 @@ class BetterSelectingZoom(AbstractOverlay, BetterZoom): event_state = Enum("normal", "selecting", "pre_selecting") # The (x,y) screen point where the mouse went down. - _screen_start = Union(None, Tuple) + _screen_start = Optional(Tuple) # The (x,,y) screen point of the last seen mouse move event. - _screen_end = Union(None, Tuple) + _screen_end = Optional(Tuple) # If **always_on** is False, this attribute indicates whether the tool # is currently enabled. diff --git a/chaco/tools/image_inspector_tool.py b/chaco/tools/image_inspector_tool.py index b4fc2213b..c98afdb09 100644 --- a/chaco/tools/image_inspector_tool.py +++ b/chaco/tools/image_inspector_tool.py @@ -17,6 +17,7 @@ # Chaco imports from chaco.abstract_overlay import AbstractOverlay +from chaco.chaco_traits import Optional from chaco.overlays.text_box_overlay import TextBoxOverlay from chaco.plots.image_plot import ImagePlot @@ -42,7 +43,7 @@ class ImageInspectorTool(BaseTool): # Stores the value of self.visible when the mouse leaves the tool, # so that it can be restored when the mouse enters again. - _old_visible = Union(None, Bool) + _old_visible = Optional(Bool) def normal_key_pressed(self, event): if self.inspector_key.match(event): diff --git a/chaco/tools/line_segment_tool.py b/chaco/tools/line_segment_tool.py index 5cf2f7479..7073885bd 100644 --- a/chaco/tools/line_segment_tool.py +++ b/chaco/tools/line_segment_tool.py @@ -21,6 +21,7 @@ # Chaco imports from chaco.abstract_overlay import AbstractOverlay +from chaco.chaco_traits import Optional class LineSegmentTool(AbstractOverlay): @@ -55,10 +56,10 @@ class LineSegmentTool(AbstractOverlay): #: The data (index, value) position of the mouse cursor; this is used by various #: draw() routines. - mouse_position = Union(None, Tuple) + mouse_position = Optional(Tuple) # The index of the vertex being dragged, if any. - _dragged = Union(None, Int) + _dragged = Optional(Int) # Is the point being dragged is a newly placed point? This informs the # "dragging" state about what to do if the user presses Escape while diff --git a/chaco/tools/range_selection.py b/chaco/tools/range_selection.py index 88f2c0c57..bb132bee8 100644 --- a/chaco/tools/range_selection.py +++ b/chaco/tools/range_selection.py @@ -34,6 +34,7 @@ # Chaco imports from chaco.abstract_controller import AbstractController +from chaco.chaco_traits import Optional class RangeSelection(AbstractController): @@ -153,7 +154,7 @@ class RangeSelection(AbstractController): _mapper = Any() # Shadow trait for the **axis_index** property. - _axis_index = Union(None, Int) + _axis_index = Optional(Int) # The data space start and end coordinates of the selected region, # expressed as an array. diff --git a/chaco/transform_color_mapper.py b/chaco/transform_color_mapper.py index c5b4aebc1..4ed9c34fb 100644 --- a/chaco/transform_color_mapper.py +++ b/chaco/transform_color_mapper.py @@ -13,6 +13,7 @@ from chaco.color_mapper import ColorMapper from traits.api import Callable, Tuple, Float, observe, Union +from .chaco_traits import Optional from .speedups import map_colors, map_colors_uint8 @@ -35,12 +36,12 @@ class TransformColorMapper(ColorMapper): unit interval [0,1] to itself (e.g. x^2 or sin(pi*x/2)). """ - data_func = Union(None, Callable) + data_func = Optional(Callable) - unit_func = Union(None, Callable) + unit_func = Optional(Callable) transformed_bounds = Tuple( - Union(None, Float), Union(None, Float) + Optional(Float), Optional(Float) ) # ------------------------------------------------------------------- diff --git a/examples/demo/canvas/axis_tool.py b/examples/demo/canvas/axis_tool.py index 7e4912584..067c679aa 100644 --- a/examples/demo/canvas/axis_tool.py +++ b/examples/demo/canvas/axis_tool.py @@ -1,4 +1,5 @@ from enable.api import BaseTool, ColorTrait +from chaco.chaco_traits import Optional from traits.api import ( Any, Bool, @@ -64,7 +65,7 @@ class AxisTool(BaseTool): down_tick_label_color = ColorTrait("red") down_bgcolor = ColorTrait("lightgray") down_border_visible = Bool(True) - down_border_color = Union(None, ColorTrait) + down_border_color = Optional(ColorTrait) _cached_tick_color = ColorTrait _cached_axis_line_color = ColorTrait diff --git a/examples/demo/canvas/mptools.py b/examples/demo/canvas/mptools.py index 07d4c8361..08e4efbca 100644 --- a/examples/demo/canvas/mptools.py +++ b/examples/demo/canvas/mptools.py @@ -18,6 +18,7 @@ # Chaco imports from chaco.api import BaseTool +from chaco.chaco_traits import Optional from chaco.tools.api import PanTool, DragZoom, LegendTool, RangeSelection @@ -65,11 +66,11 @@ class MPDragZoom(DragZoom): speed = 1.0 # The original dataspace points where blobs 1 and 2 went down - _orig_low = CArray # Union(None, Tuple) - _orig_high = CArray # Union(None, Tuple) + _orig_low = CArray # Optional(Tuple) + _orig_high = CArray # Optional(Tuple) # Dataspace center of the zoom action - _center_pt = Union(None, Tuple) + _center_pt = Optional(Tuple) # Maps blob ID numbers to the (x,y) coordinates that came in. _blobs = Dict() diff --git a/examples/demo/canvas/plot_clone_tool.py b/examples/demo/canvas/plot_clone_tool.py index ba90ac33c..010b5cf19 100644 --- a/examples/demo/canvas/plot_clone_tool.py +++ b/examples/demo/canvas/plot_clone_tool.py @@ -9,6 +9,7 @@ # Chaco imports from chaco.api import AbstractOverlay +from chaco.chaco_traits import Optional from enable.tools.api import DragTool @@ -34,7 +35,7 @@ class PlotCloneTool(AbstractOverlay, DragTool): capture_mouse = True # The (x,y) position of the "last" mouse position we received - _offset = Union(None, Tuple) + _offset = Optional(Tuple) # The relative position of the mouse_down_position to the origin # of the plot's coordinate system diff --git a/examples/demo/canvas/transient_plot_overlay.py b/examples/demo/canvas/transient_plot_overlay.py index 0384625d8..d56e7a3f8 100644 --- a/examples/demo/canvas/transient_plot_overlay.py +++ b/examples/demo/canvas/transient_plot_overlay.py @@ -2,6 +2,7 @@ from traits.api import Enum, Float, Instance, Tuple, Union from chaco.api import AbstractOverlay, BasePlotContainer +from chaco.chaco_traits import Optional class TransientPlotOverlay(BasePlotContainer, AbstractOverlay): @@ -19,7 +20,7 @@ class TransientPlotOverlay(BasePlotContainer, AbstractOverlay): margin = Float(10) # An offset to apply in X and Y - offset = Union(None, Tuple) + offset = Optional(Tuple) # Override default values of some inherited traits unified_draw = True From e6b4af05a8f2a1f43a959cde33df68ace726b950 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Fri, 14 Oct 2022 17:37:22 +0100 Subject: [PATCH 2/8] Remove unneeded Union imports --- chaco/overlays/scatter_inspector_overlay.py | 2 +- examples/demo/canvas/mptools.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/chaco/overlays/scatter_inspector_overlay.py b/chaco/overlays/scatter_inspector_overlay.py index c7969df2a..d1ee9fccd 100644 --- a/chaco/overlays/scatter_inspector_overlay.py +++ b/chaco/overlays/scatter_inspector_overlay.py @@ -13,7 +13,7 @@ # Enthought library imports from enable.api import ColorTrait, MarkerTrait -from traits.api import Float, Int, Str, Union +from traits.api import Float, Int, Str from traits.observation.events import TraitChangeEvent # Local, relative imports diff --git a/examples/demo/canvas/mptools.py b/examples/demo/canvas/mptools.py index 08e4efbca..83a6d6aea 100644 --- a/examples/demo/canvas/mptools.py +++ b/examples/demo/canvas/mptools.py @@ -13,7 +13,6 @@ Property, Tuple, CArray, - Union, ) # Chaco imports From d42ce327afc70767abacdaf272aa0459cb7bac26 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Fri, 14 Oct 2022 17:38:28 +0100 Subject: [PATCH 3/8] Skip None when looking for mapped traits. --- chaco/chaco_traits.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/chaco/chaco_traits.py b/chaco/chaco_traits.py index 9416bb0e6..f95b54211 100644 --- a/chaco/chaco_traits.py +++ b/chaco/chaco_traits.py @@ -39,6 +39,8 @@ def __init__(self, *traits, **metadata): post_setattrs = [] mapped_traits = [] for trait in traits: + if trait is None: + continue post_setattr = getattr(trait, "post_setattr", None) if post_setattr is not None: post_setattrs.append(post_setattr) From 51c3182aa2dc20569b61934769866d9cb8d1c418 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Fri, 14 Oct 2022 17:48:30 +0100 Subject: [PATCH 4/8] Fix overeager use of Optional() --- chaco/base_contour_plot.py | 3 +-- chaco/grid.py | 2 +- chaco/plot_containers.py | 3 +-- chaco/transform_color_mapper.py | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/chaco/base_contour_plot.py b/chaco/base_contour_plot.py index 9616a4920..be532e624 100644 --- a/chaco/base_contour_plot.py +++ b/chaco/base_contour_plot.py @@ -27,7 +27,6 @@ # Local relative imports from .base_2d_plot import Base2DPlot -from .chaco_traits import Optional from .color_mapper import ColorMapper @@ -53,7 +52,7 @@ class BaseContourPlot(Base2DPlot): #: colors is shorter than the number of levels, the values are repeated #: from the beginning of the list. Default is black. #: Colors are associated with levels of increasing value. - colors = Optional(Str, Instance(ColorMapper), List, Tuple) + colors = Union(Str, Instance(ColorMapper), List, Tuple) #: If present, the color mapper for the colorbar to look at. color_mapper = Property(Instance(ColorMapper)) diff --git a/chaco/grid.py b/chaco/grid.py index 342e765fd..4a1b1f538 100644 --- a/chaco/grid.py +++ b/chaco/grid.py @@ -144,7 +144,7 @@ class PlotGrid(AbstractOverlay): #: Callable : Function that takes an array of dataspace grid ticks #: and returns either an array of shape (N,2) of (starts,ends) #: for each grid point or a single tuple (low, high) - transverse_bounds = Optional(Tuple, Callable) + transverse_bounds = Union(Tuple, Callable) #: Mapper in the direction corresponding to self.orientation, i.e. transverse #: to the direction of self.mapper. This is used to compute the screen diff --git a/chaco/plot_containers.py b/chaco/plot_containers.py index 5662b2e6c..d58e776b9 100644 --- a/chaco/plot_containers.py +++ b/chaco/plot_containers.py @@ -47,7 +47,6 @@ # Local relative imports from .base_plot_container import BasePlotContainer -from .chaco_traits import Optional __all__ = [ @@ -157,7 +156,7 @@ class GridPlotContainer(BasePlotContainer): #: The amount of space to put on either side of each component, expressed #: as a tuple (h_spacing, v_spacing). - spacing = Optional(Tuple, List, Array) + spacing = Union(Tuple, List, Array) #: The vertical alignment of objects that don't span the full height. valign = Enum("bottom", "top", "center") diff --git a/chaco/transform_color_mapper.py b/chaco/transform_color_mapper.py index 4ed9c34fb..61b8e2c56 100644 --- a/chaco/transform_color_mapper.py +++ b/chaco/transform_color_mapper.py @@ -11,7 +11,7 @@ from numpy import clip, isinf, ones_like, empty from chaco.color_mapper import ColorMapper -from traits.api import Callable, Tuple, Float, observe, Union +from traits.api import Callable, Tuple, Float, observe from .chaco_traits import Optional from .speedups import map_colors, map_colors_uint8 From b24bc920edb2176d20747845f6a4cb0a557027de Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Fri, 14 Oct 2022 17:55:19 +0100 Subject: [PATCH 5/8] Fix Union() usage correctly! --- chaco/base_contour_plot.py | 2 +- chaco/grid.py | 2 +- chaco/plot_containers.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/chaco/base_contour_plot.py b/chaco/base_contour_plot.py index be532e624..9f2adf429 100644 --- a/chaco/base_contour_plot.py +++ b/chaco/base_contour_plot.py @@ -52,7 +52,7 @@ class BaseContourPlot(Base2DPlot): #: colors is shorter than the number of levels, the values are repeated #: from the beginning of the list. Default is black. #: Colors are associated with levels of increasing value. - colors = Union(Str, Instance(ColorMapper), List, Tuple) + colors = Union(None, Str, Instance(ColorMapper), List, Tuple) #: If present, the color mapper for the colorbar to look at. color_mapper = Property(Instance(ColorMapper)) diff --git a/chaco/grid.py b/chaco/grid.py index 4a1b1f538..341e7ee27 100644 --- a/chaco/grid.py +++ b/chaco/grid.py @@ -144,7 +144,7 @@ class PlotGrid(AbstractOverlay): #: Callable : Function that takes an array of dataspace grid ticks #: and returns either an array of shape (N,2) of (starts,ends) #: for each grid point or a single tuple (low, high) - transverse_bounds = Union(Tuple, Callable) + transverse_bounds = Union(None, Tuple, Callable) #: Mapper in the direction corresponding to self.orientation, i.e. transverse #: to the direction of self.mapper. This is used to compute the screen diff --git a/chaco/plot_containers.py b/chaco/plot_containers.py index d58e776b9..53d40249c 100644 --- a/chaco/plot_containers.py +++ b/chaco/plot_containers.py @@ -169,7 +169,7 @@ class GridPlotContainer(BasePlotContainer): #: specification. If there are fewer components than cells, the remaining #: cells are filled in with spaces. If there are more components than cells, #: the remainder wrap onto new rows as appropriate. - shape = Union(Tuple((0, 0)), List, Array) + shape = Union(None, Tuple((0, 0)), List, Array) #: This property exposes the underlying grid structure of the container, #: and is the preferred way of setting and reading its contents. From 7d585b6921c27f81494195f6222c200b11284ec6 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Fri, 14 Oct 2022 17:58:47 +0100 Subject: [PATCH 6/8] Fixed the wrong trait in plot_container.py --- chaco/plot_containers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chaco/plot_containers.py b/chaco/plot_containers.py index 53d40249c..7ee0046e6 100644 --- a/chaco/plot_containers.py +++ b/chaco/plot_containers.py @@ -156,7 +156,7 @@ class GridPlotContainer(BasePlotContainer): #: The amount of space to put on either side of each component, expressed #: as a tuple (h_spacing, v_spacing). - spacing = Union(Tuple, List, Array) + spacing = Union(None, Tuple, List, Array) #: The vertical alignment of objects that don't span the full height. valign = Enum("bottom", "top", "center") @@ -169,7 +169,7 @@ class GridPlotContainer(BasePlotContainer): #: specification. If there are fewer components than cells, the remaining #: cells are filled in with spaces. If there are more components than cells, #: the remainder wrap onto new rows as appropriate. - shape = Union(None, Tuple((0, 0)), List, Array) + shape = Union(Tuple((0, 0)), List, Array) #: This property exposes the underlying grid structure of the container, #: and is the preferred way of setting and reading its contents. From 76d75873cf675bdc7b72071c91a2b96459624332 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Mon, 17 Oct 2022 12:33:44 +0100 Subject: [PATCH 7/8] Add tests and some minor tweaks coming from tests. --- chaco/chaco_traits.py | 6 +- chaco/tests/test_chaco_traits.py | 117 +++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+), 3 deletions(-) create mode 100644 chaco/tests/test_chaco_traits.py diff --git a/chaco/chaco_traits.py b/chaco/chaco_traits.py index f95b54211..2408aab08 100644 --- a/chaco/chaco_traits.py +++ b/chaco/chaco_traits.py @@ -68,11 +68,11 @@ def _post_setattr(self, object, name, value): try: post_setattr(object, name, value) return - except TraitError: + except Exception: pass - # I am not sure about this - setattr(object, name + "_", value) + if self.is_mapped: + setattr(object, name + "_", value) class Optional(MappedUnion): diff --git a/chaco/tests/test_chaco_traits.py b/chaco/tests/test_chaco_traits.py new file mode 100644 index 000000000..f0337510e --- /dev/null +++ b/chaco/tests/test_chaco_traits.py @@ -0,0 +1,117 @@ +# (C) Copyright 2005-2021 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +import unittest + +from traits.api import Float, HasTraits, Int, Map, Str +from enable.api import ColorTrait + +from ..chaco_traits import MappedUnion, Optional + + +class UsesMappedUnion(HasTraits): + + no_mapped = MappedUnion(Int, Float) + + mapped = MappedUnion( + None, Str, Map({'yes': True, 'no': False}), ColorTrait + ) + + optional = Optional(ColorTrait) + + optional_default = Optional(ColorTrait, default_value='red') + + +class TestMappedUnion(unittest.TestCase): + + def test_no_mapped(self): + no_mapped = MappedUnion(Int, Float) + + self.assertFalse(no_mapped.is_mapped) + self.assertIsNone(no_mapped.post_setattr) + + def test_mapped(self): + mapped = MappedUnion(None, Str, Map({'yes': True, 'no': False})) + + self.assertTrue(mapped.is_mapped) + self.assertIsNotNone(mapped.post_setattr) + + def test_optional(self): + mapped = Optional(ColorTrait) + + self.assertTrue(mapped.is_mapped) + self.assertIsNotNone(mapped.post_setattr) + + def test_no_mapped_class(self): + mapped_union = UsesMappedUnion() + + no_mapped = mapped_union.trait('no_mapped') + + self.assertFalse(no_mapped.is_mapped) + self.assertIsNone(no_mapped.post_setattr) + + self.assertFalse(hasattr(mapped_union, 'no_mapped_')) + + mapped_union.no_mapped = 1 + + self.assertFalse(hasattr(mapped_union, 'no_mapped_')) + + def test_mapped_class(self): + mapped_union = UsesMappedUnion() + + mapped = mapped_union.trait('mapped') + + self.assertTrue(mapped.is_mapped) + self.assertIsNotNone(mapped.post_setattr) + + # test default + self.assertIsNone(mapped_union.mapped_) + + # test mapper works + mapped_union.mapped = 'yes' + + self.assertTrue(mapped_union.mapped_) + + # test second mapper works + mapped_union.mapped = 'red' + + self.assertEqual(mapped_union.mapped_, (1.0, 0.0, 0.0, 1.0)) + + # test non-mapped value works + mapped_union.mapped = 'notacolor' + + self.assertEqual(mapped_union.mapped_, 'notacolor') + + def test_optional_class(self): + mapped_union = UsesMappedUnion() + + optional = mapped_union.trait('optional') + + self.assertTrue(optional.is_mapped) + self.assertIsNotNone(optional.post_setattr) + + # test default + self.assertIsNone(mapped_union.optional_) + + # test mapper works + mapped_union.optional = 'red' + + self.assertEqual(mapped_union.optional_, (1.0, 0.0, 0.0, 1.0)) + + # test non-mapped value works + mapped_union.optional = None + + self.assertIsNone(mapped_union.optional_) + + def test_optional_default_class(self): + mapped_union = UsesMappedUnion() + + # test default + self.assertEqual(mapped_union.optional_default_, (1.0, 0.0, 0.0, 1.0)) From 47ada7db293156e6e0cf5014a8d65d3423209c7c Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Tue, 25 Oct 2022 10:58:54 +0100 Subject: [PATCH 8/8] Change based on PR review comments. --- examples/demo/canvas/mptools.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/demo/canvas/mptools.py b/examples/demo/canvas/mptools.py index 83a6d6aea..77e13a22a 100644 --- a/examples/demo/canvas/mptools.py +++ b/examples/demo/canvas/mptools.py @@ -65,8 +65,8 @@ class MPDragZoom(DragZoom): speed = 1.0 # The original dataspace points where blobs 1 and 2 went down - _orig_low = CArray # Optional(Tuple) - _orig_high = CArray # Optional(Tuple) + _orig_low = CArray + _orig_high = CArray # Dataspace center of the zoom action _center_pt = Optional(Tuple)