We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Zoom broken in some demos
**Problem Description**
This bug affects the following demos:
* `shell/contour.py`
* `shell/contourf.py`
* `basic/contour_plot.py`

Mouse-scroll doesn't zoom the plot until the user pans the plot. After panning, the plot is really zoomed out.

**Reproduction Steps:**
Mouse-scroll, then left-drag.

```python
python shell/contour.py
```

**Expected behavior:**
Plot should zoom immediately on mouse-scroll.

**OS, Python version:**
MacOSX 10.14
Python3.6

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `chaco/base_contour_plot.py`
Content:
```
1 import six
2 
3 from numpy import array, isscalar, issubsctype, linspace, number
4 
5 # Enthought library imports
6 from enable.api import ColorTrait
7 from traits.api import Bool, Instance, Int, List, Property, \
8         Range, Str, Trait, Tuple
9 
10 # Local relative imports
11 from .base_2d_plot import Base2DPlot
12 from .color_mapper import ColorMapper
13 
14 
15 class BaseContourPlot(Base2DPlot):
16     """ The base class for contour plots.  Mostly manages configuration and
17     change events with colormap and contour parameters.
18     """
19 
20     #------------------------------------------------------------------------
21     # Data-related traits
22     #------------------------------------------------------------------------
23 
24     # Defines the levels to contour.
25     # ``levels`` can be either: a list of floating point numbers that define
26     # the value of the function at the contours; a positive integer, in which
27     # case the range of the value is divided in the given number of equally
28     # spaced levels; or "auto" (default), which divides the range in 10 levels
29     levels = Trait("auto", Int, List)
30 
31     # The color(s) of the lines.
32     # ``colors`` can be given as a color name, in which case all contours have
33     # the same color, as a list of colors, or as a colormap. If the list of
34     # colors is shorter than the number of levels, the values are repeated
35     # from the beginning of the list. Default is black.
36     # Colors are associated with levels of increasing value.
37     colors = Trait(None, Str, Instance(ColorMapper), List, Tuple)
38 
39     # If present, the color mapper for the colorbar to look at.
40     color_mapper = Property(Instance(ColorMapper))
41 
42     # A global alpha value to apply to all the contours
43     alpha = Trait(1.0, Range(0.0, 1.0))
44 
45     #------------------------------------------------------------------------
46     # Private traits
47     #------------------------------------------------------------------------
48 
49     # Is the cached level data valid?
50     _level_cache_valid = Bool(False)
51 
52     # Is the cached color data valid?
53     _colors_cache_valid = Bool(False)
54 
55     # List of levels and their associated line properties.
56     _levels = List
57 
58     # List of colors
59     _colors = List
60 
61     # Mapped trait used to convert user-suppied color values to AGG-acceptable
62     # ones. (Mapped traits in lists are not supported, must be converted one at
63     # a time.)
64     _color_map_trait = ColorTrait
65 
66 
67     def __init__(self, *args, **kwargs):
68         super(BaseContourPlot, self).__init__(*args, **kwargs)
69         if self.color_mapper:
70             self.color_mapper.on_trait_change(self._update_color_mapper, "updated")
71         return
72 
73     def _update_levels(self):
74         """ Updates the levels cache.  """
75         low, high = self.value.get_bounds()
76         if self.levels == "auto":
77             self._levels = list(linspace(low, high, 10))
78         elif isinstance(self.levels, int):
79             self._levels = list(linspace(low, high, self.levels))
80         else:
81             self._levels = self.levels
82             self._levels.sort()
83         self._level_cache_valid = True
84         self._colors_cache_valid = False
85 
86     def _update_colors(self, numcolors=None):
87         """ Update the colors cache using our color mapper and based
88         on our number of levels.  The **mode** parameter accounts for fenceposting:
89           - If **mode** is "poly", then the number of colors to generate is 1
90             less than the number of levels
91           - If **mode** is "line", then the number of colors to generate is
92             equal to the number of levels
93         """
94         if numcolors is None:
95             numcolors = len(self._levels)
96 
97         colors = self.colors
98         # If we are given no colors, set a default for all levels
99         if colors is None:
100             self._color_map_trait = "black"
101             self._colors = [self._color_map_trait_] * numcolors
102 
103         # If we are given a single color, apply it to all levels
104         elif isinstance(colors, six.string_types):
105             self._color_map_trait = colors
106             self._colors = [self._color_map_trait_] * numcolors
107 
108         # If we are given a colormap, use it to map all the levels to colors
109         elif isinstance(colors, ColorMapper):
110             self._colors =  []
111             mapped_colors = self.color_mapper.map_screen(array(self._levels))
112             for i in range(numcolors):
113                 self._color_map_trait = tuple(mapped_colors[i])
114                 self._colors.append(self._color_map_trait_)
115 
116         # A list or tuple
117         # This could be a length 3 or 4 sequence of scalars, which indicates
118         # a color; otherwise, this is interpreted as a list of items to
119         # be converted via self._color_map_trait.
120         else:
121             if len(colors) in (3,4) and \
122                     (isscalar(colors[0]) and issubsctype(type(colors[0]), number)):
123                 self._color_map_trait = colors
124                 self._colors = [self._color_map_trait_] * numcolors
125             else:
126                 # if the list of colors is shorter than the list of levels, simply
127                 # repeat colors from the beginning of the list as needed
128                 self._colors = []
129                 for i in range(len(self._levels)):
130                     self._color_map_trait = colors[i%len(colors)]
131                     self._colors.append(self._color_map_trait_)
132 
133         self._colors_cache_valid = True
134         return
135 
136 
137     #------------------------------------------------------------------------
138     # Event handlers
139     #------------------------------------------------------------------------
140 
141     def _index_data_changed_fired(self):
142         # If the index data has changed, the reset the levels cache (which
143         # also triggers all the other caches to reset).
144         self._level_cache_valid = False
145         self.invalidate_draw()
146 
147     def _value_data_changed_fired(self):
148         # If the index data has changed, the reset the levels cache (which
149         # also triggers all the other caches to reset).
150         self._level_cache_valid = False
151         self.invalidate_draw()
152 
153     def _index_mapper_changed_fired(self):
154         # If the index mapper has changed, then we need to redraw
155         self.invalidate_draw()
156 
157     def _update_color_mapper(self):
158         # If the color mapper has changed, then we need to recompute the
159         # levels and cached data associated with that.
160         self._level_cache_valid = False
161         self.invalidate_draw()
162 
163     def _levels_changed(self):
164         self._update_levels()
165         self.invalidate_draw()
166         self.request_redraw()
167 
168     def _colors_changed(self):
169         if self._level_cache_valid:
170             self._update_colors()
171             self.invalidate_draw()
172 
173     #------------------------------------------------------------------------
174     # Trait properties
175     #------------------------------------------------------------------------
176 
177     def _get_color_mapper(self):
178         if isinstance(self.colors, ColorMapper):
179             return self.colors
180         else:
181             return None
182 
183     def _set_color_mapper(self, color_mapper):
184         # Remove the dynamic event handler from the old color mapper
185         if self.colors is not None and isinstance(self.colors, ColorMapper):
186             self.colors.on_trait_change(self._update_color_mapper, "updated", remove=True)
187 
188             # Check to see if we should copy over the range as well
189             if color_mapper is not None:
190                 if color_mapper.range is None and self.colors.range is not None:
191                     color_mapper.range = self.colors.range
192 
193         # Attach the dynamic event handler to the new color mapper
194         if color_mapper is not None:
195             color_mapper.on_trait_change(self._update_color_mapper, "updated")
196 
197         self.colors = color_mapper
198         self._update_color_mapper()
199
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/chaco/base_contour_plot.py b/chaco/base_contour_plot.py
--- a/chaco/base_contour_plot.py
+++ b/chaco/base_contour_plot.py
@@ -142,33 +142,32 @@
         # If the index data has changed, the reset the levels cache (which
         # also triggers all the other caches to reset).
         self._level_cache_valid = False
-        self.invalidate_draw()
+        self.invalidate_and_redraw()
 
     def _value_data_changed_fired(self):
         # If the index data has changed, the reset the levels cache (which
         # also triggers all the other caches to reset).
         self._level_cache_valid = False
-        self.invalidate_draw()
+        self.invalidate_and_redraw()
 
     def _index_mapper_changed_fired(self):
         # If the index mapper has changed, then we need to redraw
-        self.invalidate_draw()
+        self.invalidate_and_redraw()
 
     def _update_color_mapper(self):
         # If the color mapper has changed, then we need to recompute the
         # levels and cached data associated with that.
         self._level_cache_valid = False
-        self.invalidate_draw()
+        self.invalidate_and_redraw()
 
     def _levels_changed(self):
         self._update_levels()
-        self.invalidate_draw()
-        self.request_redraw()
+        self.invalidate_and_redraw()
 
     def _colors_changed(self):
         if self._level_cache_valid:
             self._update_colors()
-            self.invalidate_draw()
+            self.invalidate_and_redraw()
 
     #------------------------------------------------------------------------
     # Trait properties

{"golden_diff": "diff --git a/chaco/base_contour_plot.py b/chaco/base_contour_plot.py\n--- a/chaco/base_contour_plot.py\n+++ b/chaco/base_contour_plot.py\n@@ -142,33 +142,32 @@\n         # If the index data has changed, the reset the levels cache (which\n         # also triggers all the other caches to reset).\n         self._level_cache_valid = False\n-        self.invalidate_draw()\n+        self.invalidate_and_redraw()\n \n     def _value_data_changed_fired(self):\n         # If the index data has changed, the reset the levels cache (which\n         # also triggers all the other caches to reset).\n         self._level_cache_valid = False\n-        self.invalidate_draw()\n+        self.invalidate_and_redraw()\n \n     def _index_mapper_changed_fired(self):\n         # If the index mapper has changed, then we need to redraw\n-        self.invalidate_draw()\n+        self.invalidate_and_redraw()\n \n     def _update_color_mapper(self):\n         # If the color mapper has changed, then we need to recompute the\n         # levels and cached data associated with that.\n         self._level_cache_valid = False\n-        self.invalidate_draw()\n+        self.invalidate_and_redraw()\n \n     def _levels_changed(self):\n         self._update_levels()\n-        self.invalidate_draw()\n-        self.request_redraw()\n+        self.invalidate_and_redraw()\n \n     def _colors_changed(self):\n         if self._level_cache_valid:\n             self._update_colors()\n-            self.invalidate_draw()\n+            self.invalidate_and_redraw()\n \n     #------------------------------------------------------------------------\n     # Trait properties\n", "issue": "Zoom broken in some demos\n**Problem Description**\r\nThis bug affects the following demos:\r\n* `shell/contour.py`\r\n* `shell/contourf.py`\r\n* `basic/contour_plot.py`\r\n\r\nMouse-scroll doesn't zoom the plot until the user pans the plot. After panning, the plot is really zoomed out.\r\n\r\n**Reproduction Steps:**\r\nMouse-scroll, then left-drag.\r\n\r\n```python\r\npython shell/contour.py\r\n```\r\n\r\n**Expected behavior:**\r\nPlot should zoom immediately on mouse-scroll.\r\n\r\n**OS, Python version:**\r\nMacOSX 10.14\r\nPython3.6\n", "before_files": [{"content": "import six\n\nfrom numpy import array, isscalar, issubsctype, linspace, number\n\n# Enthought library imports\nfrom enable.api import ColorTrait\nfrom traits.api import Bool, Instance, Int, List, Property, \\\n        Range, Str, Trait, Tuple\n\n# Local relative imports\nfrom .base_2d_plot import Base2DPlot\nfrom .color_mapper import ColorMapper\n\n\nclass BaseContourPlot(Base2DPlot):\n    \"\"\" The base class for contour plots.  Mostly manages configuration and\n    change events with colormap and contour parameters.\n    \"\"\"\n\n    #------------------------------------------------------------------------\n    # Data-related traits\n    #------------------------------------------------------------------------\n\n    # Defines the levels to contour.\n    # ``levels`` can be either: a list of floating point numbers that define\n    # the value of the function at the contours; a positive integer, in which\n    # case the range of the value is divided in the given number of equally\n    # spaced levels; or \"auto\" (default), which divides the range in 10 levels\n    levels = Trait(\"auto\", Int, List)\n\n    # The color(s) of the lines.\n    # ``colors`` can be given as a color name, in which case all contours have\n    # the same color, as a list of colors, or as a colormap. If the list of\n    # colors is shorter than the number of levels, the values are repeated\n    # from the beginning of the list. Default is black.\n    # Colors are associated with levels of increasing value.\n    colors = Trait(None, Str, Instance(ColorMapper), List, Tuple)\n\n    # If present, the color mapper for the colorbar to look at.\n    color_mapper = Property(Instance(ColorMapper))\n\n    # A global alpha value to apply to all the contours\n    alpha = Trait(1.0, Range(0.0, 1.0))\n\n    #------------------------------------------------------------------------\n    # Private traits\n    #------------------------------------------------------------------------\n\n    # Is the cached level data valid?\n    _level_cache_valid = Bool(False)\n\n    # Is the cached color data valid?\n    _colors_cache_valid = Bool(False)\n\n    # List of levels and their associated line properties.\n    _levels = List\n\n    # List of colors\n    _colors = List\n\n    # Mapped trait used to convert user-suppied color values to AGG-acceptable\n    # ones. (Mapped traits in lists are not supported, must be converted one at\n    # a time.)\n    _color_map_trait = ColorTrait\n\n\n    def __init__(self, *args, **kwargs):\n        super(BaseContourPlot, self).__init__(*args, **kwargs)\n        if self.color_mapper:\n            self.color_mapper.on_trait_change(self._update_color_mapper, \"updated\")\n        return\n\n    def _update_levels(self):\n        \"\"\" Updates the levels cache.  \"\"\"\n        low, high = self.value.get_bounds()\n        if self.levels == \"auto\":\n            self._levels = list(linspace(low, high, 10))\n        elif isinstance(self.levels, int):\n            self._levels = list(linspace(low, high, self.levels))\n        else:\n            self._levels = self.levels\n            self._levels.sort()\n        self._level_cache_valid = True\n        self._colors_cache_valid = False\n\n    def _update_colors(self, numcolors=None):\n        \"\"\" Update the colors cache using our color mapper and based\n        on our number of levels.  The **mode** parameter accounts for fenceposting:\n          - If **mode** is \"poly\", then the number of colors to generate is 1\n            less than the number of levels\n          - If **mode** is \"line\", then the number of colors to generate is\n            equal to the number of levels\n        \"\"\"\n        if numcolors is None:\n            numcolors = len(self._levels)\n\n        colors = self.colors\n        # If we are given no colors, set a default for all levels\n        if colors is None:\n            self._color_map_trait = \"black\"\n            self._colors = [self._color_map_trait_] * numcolors\n\n        # If we are given a single color, apply it to all levels\n        elif isinstance(colors, six.string_types):\n            self._color_map_trait = colors\n            self._colors = [self._color_map_trait_] * numcolors\n\n        # If we are given a colormap, use it to map all the levels to colors\n        elif isinstance(colors, ColorMapper):\n            self._colors =  []\n            mapped_colors = self.color_mapper.map_screen(array(self._levels))\n            for i in range(numcolors):\n                self._color_map_trait = tuple(mapped_colors[i])\n                self._colors.append(self._color_map_trait_)\n\n        # A list or tuple\n        # This could be a length 3 or 4 sequence of scalars, which indicates\n        # a color; otherwise, this is interpreted as a list of items to\n        # be converted via self._color_map_trait.\n        else:\n            if len(colors) in (3,4) and \\\n                    (isscalar(colors[0]) and issubsctype(type(colors[0]), number)):\n                self._color_map_trait = colors\n                self._colors = [self._color_map_trait_] * numcolors\n            else:\n                # if the list of colors is shorter than the list of levels, simply\n                # repeat colors from the beginning of the list as needed\n                self._colors = []\n                for i in range(len(self._levels)):\n                    self._color_map_trait = colors[i%len(colors)]\n                    self._colors.append(self._color_map_trait_)\n\n        self._colors_cache_valid = True\n        return\n\n\n    #------------------------------------------------------------------------\n    # Event handlers\n    #------------------------------------------------------------------------\n\n    def _index_data_changed_fired(self):\n        # If the index data has changed, the reset the levels cache (which\n        # also triggers all the other caches to reset).\n        self._level_cache_valid = False\n        self.invalidate_draw()\n\n    def _value_data_changed_fired(self):\n        # If the index data has changed, the reset the levels cache (which\n        # also triggers all the other caches to reset).\n        self._level_cache_valid = False\n        self.invalidate_draw()\n\n    def _index_mapper_changed_fired(self):\n        # If the index mapper has changed, then we need to redraw\n        self.invalidate_draw()\n\n    def _update_color_mapper(self):\n        # If the color mapper has changed, then we need to recompute the\n        # levels and cached data associated with that.\n        self._level_cache_valid = False\n        self.invalidate_draw()\n\n    def _levels_changed(self):\n        self._update_levels()\n        self.invalidate_draw()\n        self.request_redraw()\n\n    def _colors_changed(self):\n        if self._level_cache_valid:\n            self._update_colors()\n            self.invalidate_draw()\n\n    #------------------------------------------------------------------------\n    # Trait properties\n    #------------------------------------------------------------------------\n\n    def _get_color_mapper(self):\n        if isinstance(self.colors, ColorMapper):\n            return self.colors\n        else:\n            return None\n\n    def _set_color_mapper(self, color_mapper):\n        # Remove the dynamic event handler from the old color mapper\n        if self.colors is not None and isinstance(self.colors, ColorMapper):\n            self.colors.on_trait_change(self._update_color_mapper, \"updated\", remove=True)\n\n            # Check to see if we should copy over the range as well\n            if color_mapper is not None:\n                if color_mapper.range is None and self.colors.range is not None:\n                    color_mapper.range = self.colors.range\n\n        # Attach the dynamic event handler to the new color mapper\n        if color_mapper is not None:\n            color_mapper.on_trait_change(self._update_color_mapper, \"updated\")\n\n        self.colors = color_mapper\n        self._update_color_mapper()\n", "path": "chaco/base_contour_plot.py"}], "after_files": [{"content": "import six\n\nfrom numpy import array, isscalar, issubsctype, linspace, number\n\n# Enthought library imports\nfrom enable.api import ColorTrait\nfrom traits.api import Bool, Instance, Int, List, Property, \\\n        Range, Str, Trait, Tuple\n\n# Local relative imports\nfrom .base_2d_plot import Base2DPlot\nfrom .color_mapper import ColorMapper\n\n\nclass BaseContourPlot(Base2DPlot):\n    \"\"\" The base class for contour plots.  Mostly manages configuration and\n    change events with colormap and contour parameters.\n    \"\"\"\n\n    #------------------------------------------------------------------------\n    # Data-related traits\n    #------------------------------------------------------------------------\n\n    # Defines the levels to contour.\n    # ``levels`` can be either: a list of floating point numbers that define\n    # the value of the function at the contours; a positive integer, in which\n    # case the range of the value is divided in the given number of equally\n    # spaced levels; or \"auto\" (default), which divides the range in 10 levels\n    levels = Trait(\"auto\", Int, List)\n\n    # The color(s) of the lines.\n    # ``colors`` can be given as a color name, in which case all contours have\n    # the same color, as a list of colors, or as a colormap. If the list of\n    # colors is shorter than the number of levels, the values are repeated\n    # from the beginning of the list. Default is black.\n    # Colors are associated with levels of increasing value.\n    colors = Trait(None, Str, Instance(ColorMapper), List, Tuple)\n\n    # If present, the color mapper for the colorbar to look at.\n    color_mapper = Property(Instance(ColorMapper))\n\n    # A global alpha value to apply to all the contours\n    alpha = Trait(1.0, Range(0.0, 1.0))\n\n    #------------------------------------------------------------------------\n    # Private traits\n    #------------------------------------------------------------------------\n\n    # Is the cached level data valid?\n    _level_cache_valid = Bool(False)\n\n    # Is the cached color data valid?\n    _colors_cache_valid = Bool(False)\n\n    # List of levels and their associated line properties.\n    _levels = List\n\n    # List of colors\n    _colors = List\n\n    # Mapped trait used to convert user-suppied color values to AGG-acceptable\n    # ones. (Mapped traits in lists are not supported, must be converted one at\n    # a time.)\n    _color_map_trait = ColorTrait\n\n\n    def __init__(self, *args, **kwargs):\n        super(BaseContourPlot, self).__init__(*args, **kwargs)\n        if self.color_mapper:\n            self.color_mapper.on_trait_change(self._update_color_mapper, \"updated\")\n        return\n\n    def _update_levels(self):\n        \"\"\" Updates the levels cache.  \"\"\"\n        low, high = self.value.get_bounds()\n        if self.levels == \"auto\":\n            self._levels = list(linspace(low, high, 10))\n        elif isinstance(self.levels, int):\n            self._levels = list(linspace(low, high, self.levels))\n        else:\n            self._levels = self.levels\n            self._levels.sort()\n        self._level_cache_valid = True\n        self._colors_cache_valid = False\n\n    def _update_colors(self, numcolors=None):\n        \"\"\" Update the colors cache using our color mapper and based\n        on our number of levels.  The **mode** parameter accounts for fenceposting:\n          - If **mode** is \"poly\", then the number of colors to generate is 1\n            less than the number of levels\n          - If **mode** is \"line\", then the number of colors to generate is\n            equal to the number of levels\n        \"\"\"\n        if numcolors is None:\n            numcolors = len(self._levels)\n\n        colors = self.colors\n        # If we are given no colors, set a default for all levels\n        if colors is None:\n            self._color_map_trait = \"black\"\n            self._colors = [self._color_map_trait_] * numcolors\n\n        # If we are given a single color, apply it to all levels\n        elif isinstance(colors, six.string_types):\n            self._color_map_trait = colors\n            self._colors = [self._color_map_trait_] * numcolors\n\n        # If we are given a colormap, use it to map all the levels to colors\n        elif isinstance(colors, ColorMapper):\n            self._colors =  []\n            mapped_colors = self.color_mapper.map_screen(array(self._levels))\n            for i in range(numcolors):\n                self._color_map_trait = tuple(mapped_colors[i])\n                self._colors.append(self._color_map_trait_)\n\n        # A list or tuple\n        # This could be a length 3 or 4 sequence of scalars, which indicates\n        # a color; otherwise, this is interpreted as a list of items to\n        # be converted via self._color_map_trait.\n        else:\n            if len(colors) in (3,4) and \\\n                    (isscalar(colors[0]) and issubsctype(type(colors[0]), number)):\n                self._color_map_trait = colors\n                self._colors = [self._color_map_trait_] * numcolors\n            else:\n                # if the list of colors is shorter than the list of levels, simply\n                # repeat colors from the beginning of the list as needed\n                self._colors = []\n                for i in range(len(self._levels)):\n                    self._color_map_trait = colors[i%len(colors)]\n                    self._colors.append(self._color_map_trait_)\n\n        self._colors_cache_valid = True\n        return\n\n\n    #------------------------------------------------------------------------\n    # Event handlers\n    #------------------------------------------------------------------------\n\n    def _index_data_changed_fired(self):\n        # If the index data has changed, the reset the levels cache (which\n        # also triggers all the other caches to reset).\n        self._level_cache_valid = False\n        self.invalidate_and_redraw()\n\n    def _value_data_changed_fired(self):\n        # If the index data has changed, the reset the levels cache (which\n        # also triggers all the other caches to reset).\n        self._level_cache_valid = False\n        self.invalidate_and_redraw()\n\n    def _index_mapper_changed_fired(self):\n        # If the index mapper has changed, then we need to redraw\n        self.invalidate_and_redraw()\n\n    def _update_color_mapper(self):\n        # If the color mapper has changed, then we need to recompute the\n        # levels and cached data associated with that.\n        self._level_cache_valid = False\n        self.invalidate_and_redraw()\n\n    def _levels_changed(self):\n        self._update_levels()\n        self.invalidate_and_redraw()\n\n    def _colors_changed(self):\n        if self._level_cache_valid:\n            self._update_colors()\n            self.invalidate_and_redraw()\n\n    #------------------------------------------------------------------------\n    # Trait properties\n    #------------------------------------------------------------------------\n\n    def _get_color_mapper(self):\n        if isinstance(self.colors, ColorMapper):\n            return self.colors\n        else:\n            return None\n\n    def _set_color_mapper(self, color_mapper):\n        # Remove the dynamic event handler from the old color mapper\n        if self.colors is not None and isinstance(self.colors, ColorMapper):\n            self.colors.on_trait_change(self._update_color_mapper, \"updated\", remove=True)\n\n            # Check to see if we should copy over the range as well\n            if color_mapper is not None:\n                if color_mapper.range is None and self.colors.range is not None:\n                    color_mapper.range = self.colors.range\n\n        # Attach the dynamic event handler to the new color mapper\n        if color_mapper is not None:\n            color_mapper.on_trait_change(self._update_color_mapper, \"updated\")\n\n        self.colors = color_mapper\n        self._update_color_mapper()\n", "path": "chaco/base_contour_plot.py"}]}