Repository: enthought/chaco

Issue:
Empty chaco plot crashes when clicked on
**Problem Description**
Clicking on an empty chaco plot results in a crash. In our usage, an empty chaco plot is created first, and later data is added.
**Reproduction Steps:**
```python
class LinePlot(HasTraits):
    plot = Instance(Plot)
    x = []
    y = []
    traits_view = View(
            Item('plot', editor=ComponentEditor(),
                 show_label=False),
            width=500, height=500,
            resizable=True,
            title="Chaco Plot")
    def _plot_default(self):
        # Create the data and the PlotData object
        # Create a Plot and associate it with the PlotData
        plot = Plot(ArrayPlotData(x=self.x, y=self.y))
        # Create a line plot in the Plot
        plot.plot(("x", "y"), type="line", color="blue")
        # Set the title and label the x-axis
        plot.title = "sin(x) * x^3"
        plot.x_axis.title = "x-axis label"
        plot.tools.append(PanTool(plot))
        plot.overlays.append(ZoomTool(plot, zoom_fator=1.1))
        # Assign it to our self.plot attribute
        return plot
demo = LinePlot()
demo.configure_traits()
```
Once the plot comes up, try clicking on it. It crashes with the following traceback (full path has been replaced with <>).

```
Traceback (most recent call last):
  File "<>/lib/python3.6/site-packages/enable/qt4/base_window.py", line 213, in paintEvent
    self.handler.paintEvent(event)
  File "<>/lib/python3.6/site-packages/enable/qt4/base_window.py", line 65, in paintEvent
    self._enable_window._paint(event)
  File "<>/lib/python3.6/site-packages/enable/abstract_window.py", line 471, in _paint
    self.component.draw(gc, view_bounds=(0, 0, size[0], size[1]))
  File "<>/lib/python3.6/site-packages/enable/component.py", line 427, in draw
    self._draw(gc, view_bounds, mode)
  File "<>/lib/python3.6/site-packages/enable/component.py", line 779, in _draw
    self._dispatch_draw(layer, gc, view_bounds, mode)
  File "<>/lib/python3.6/site-packages/enable/container.py", line 273, in _dispatch_draw
    component._dispatch_draw(layer, gc, new_bounds, mode)
  File "<>/lib/python3.6/site-packages/enable/container.py", line 258, in _dispatch_draw
    my_handler(gc, view_bounds, mode)
  File "<>/lib/python3.6/site-packages/enable/container.py", line 298, in _draw_container_underlay
    self._draw_underlay(gc, view_bounds, mode)
  File "<>/lib/python3.6/site-packages/enable/component.py", line 887, in _draw_underlay
    underlay.overlay(self, gc, view_bounds, mode)
  File "<>/lib/python3.6/site-packages/chaco/grid.py", line 307, in overlay
    self._compute_ticks(other_component)
  File "<>/lib/python3.6/site-packages/chaco/grid.py", line 255, in _compute_ticks
    scale=scale)
  File "<>/lib/python3.6/site-packages/chaco/ticks.py", line 84, in get_ticks
    interval, use_endpoints=False), float64)
  File "<>/lib/python3.6/site-packages/chaco/ticks.py", line 216, in auto_ticks
    ticks = arange( start, end + (tick_interval / 2.0), tick_interval )
ValueError: arange: cannot compute length
```


**Expected behavior:**

No crash when an empty plot is clicked. After data is added, Zoom and Pan work.

**OS, Python version:** [Enter OS name and Python version]
Python 3.6, MacOS Catalina 10.15.6
chaco                          4.8.0-4
enable                         4.8.1-5


Discussion:
@sandhya-sago I encountered this issue as well. A workaround, not elegant at all, is to append zoom and pan tools to your components/overlays _after_ plotting some data: if you have a function to update your plot data, you can put in it something like:

```Python3
if self._zoom_tool not in self._plot.overlays:
    self._plot.overlays.append(self._zoom_tool)
```

Definitely not elegant, but prevents the crash.

Resolving patch:
diff --git a/chaco/ticks.py b/chaco/ticks.py
--- a/chaco/ticks.py
+++ b/chaco/ticks.py
@@ -24,6 +24,7 @@
     finfo,
     float64,
     floor,
+    isnan,
     linspace,
     log10,
     minimum,
@@ -242,6 +243,10 @@ def auto_ticks(
     An array of tick mark locations. The first and last tick entries are the
     axis end points.
     """
+    # if a NaN makes it here, we cant determine tick mark locations. Return
+    # empty list to prevent crash. Motivated by enthought/chaco#529
+    if isnan([data_low, data_high, bound_low, bound_high]).any():
+        return []
 
     is_auto_low = bound_low == "auto"
     is_auto_high = bound_high == "auto"


Test patch:
diff --git a/chaco/tests/test_plot.py b/chaco/tests/test_plot.py
--- a/chaco/tests/test_plot.py
+++ b/chaco/tests/test_plot.py
@@ -2,9 +2,16 @@
 
 from numpy import alltrue, arange, array
 
+from enable.api import ComponentEditor
+from enable.testing import EnableTestAssistant
+from traits.api import HasTraits, Instance
+from traits.etsconfig.api import ETSConfig
+from traitsui.api import Item, View
+
 # Chaco imports
 from chaco.api import ArrayPlotData, Plot, DataRange1D, PlotGraphicsContext
 from chaco.default_colormaps import viridis
+from chaco.tools.api import PanTool, ZoomTool
 
 
 class PlotTestCase(unittest.TestCase):
@@ -91,3 +98,38 @@ def test_text_plot(self):
         gc.render_component(plot)
         actual = gc.bmp_array[:, :, :]
         self.assertFalse(alltrue(actual == 255))
+
+
+class EmptyLinePlot(HasTraits):
+    plot = Instance(Plot)
+    x = []
+    y = []
+    traits_view = View(
+        Item('plot', editor=ComponentEditor(), show_label=False),
+        width=500,
+        height=500,
+        resizable=True
+    )
+
+    def _plot_default(self):
+        plot = Plot(ArrayPlotData(x=self.x, y=self.y))
+        plot.plot(("x", "y"), type="line", color="blue")
+        plot.tools.append(PanTool(plot))
+        plot.overlays.append(ZoomTool(plot, zoom_factor=1.1))
+        return plot
+
+
+# regression test for enthought/chaco#529
+@unittest.skipIf(ETSConfig.toolkit == "null", "Skip on 'null' toolkit")
+class TestEmptyPlot(unittest.TestCase, EnableTestAssistant):
+
+    def test_dont_crash_on_click(self):
+        from traitsui.testing.api import UITester
+        tester = UITester()
+        empty_plot = EmptyLinePlot()
+
+        with tester.create_ui(empty_plot):
+            self.press_move_release(
+                empty_plot.plot,
+                [(1, 1), (25, 25), (50, 50), (100, 100)],
+            )