Repository: enthought/chaco

Issue:
Y label gets clipped
**Problem Description**

The Y label in the following example doesn't get displayed while the X label gets displayed.

![why-chaco-why](https://user-images.githubusercontent.com/1926457/107530112-73bcc080-6bb3-11eb-920b-d4beaf1ae329.png)

**Reproduction Steps:**

```python
from numpy import linspace, sin
from traits.api import HasTraits, Instance
from traitsui.api import View, Item
from chaco.api import Plot, ArrayPlotData
from enable.api import ComponentEditor

class LinePlot(HasTraits):
    plot = Instance(Plot)

    traits_view = View(
        Item('plot',editor=ComponentEditor(), show_label=False),
        width=500, height=500, resizable=True, title="Chaco Plot")

    def _plot_default(self):
        x = linspace(-14, 14, 100)
        y = sin(x) * x**3
        plotdata = ArrayPlotData(x=x, y=y)

        plot = Plot(plotdata)
        plot.plot(("x", "y"), type="line", color="blue")
        plot.title = "sin(x) * x^3"
       
        plot.x_axis.title = "X AXIS"
        plot.y_axis.title = "Y AXIS"
       
        return plot

if __name__ == "__main__":
    LinePlot().configure_traits()
```

**Expected behavior:**

We expect the Y label to be displayed.

**OS, Python version:** Windows 10 Pro. Python 3.6.0. Chaco 4.8.0. Enable 4.8.1.

Originally reported on the ETS Users Google Group - https://groups.google.com/g/ets-users/c/gHQmFsVMVhw


Discussion:
Note that the title is actually there, but the default padding is such that most of the text is clipped.  This requires overriding the `plot.padding_left` attribute to create enough space to show the text.
This is not a bug per-se as @timdiller points out - but what might help with these problems is to perhaps adjust the _default_ values of the right and left padding based on the presence or absence of a non-empty axis title.

Or alternatively, use the edge of the padding as the guide for the location of the axis title?

Resolving patch:
diff --git a/chaco/data_view.py b/chaco/data_view.py
--- a/chaco/data_view.py
+++ b/chaco/data_view.py
@@ -3,7 +3,7 @@
 """
 from numpy import array, transpose
 
-from traits.api import Bool, Enum, Instance, Property
+from traits.api import Bool, Enum, Instance, Int, observe, Property, Union
 from enable.api import color_table
 
 from .abstract_overlay import AbstractOverlay
@@ -207,11 +207,71 @@ class DataView(OverlayPlotContainer):
     #: Background color (overrides Enable Component)
     bgcolor = "white"
 
-    #: Padding defaults.
-    padding_top = 50
-    padding_bottom = 50
-    padding_left = 50
-    padding_right = 50
+    # Padding defaults. These were converted to properties as a hacky fix for
+    # enthought/chaco#564
+    #: Top Padding default
+    padding_top = Property(
+        observe='y_axis.[title,orientation], x_axis.[title,orientation]'
+    )
+    #: Bottom Padding default
+    padding_bottom = Property(
+        observe='y_axis.[title,orientation], x_axis.[title,orientation]'
+    )
+    #: Left Padding default
+    padding_left = Property(
+        observe='y_axis.[title,orientation], x_axis.[title,orientation]'
+    )
+    #: Right Padding default
+    padding_right = Property(
+        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())
+
+    def _find_padding(self, side):
+        SIDE_TO_TRAIT_MAP = {
+            "top": "_padding_top",
+            "bottom": "_padding_bottom",
+            "left": "_padding_left",
+            "right": "_padding_right",
+        }
+        if getattr(self, SIDE_TO_TRAIT_MAP[side]) is not None:
+            return getattr(self, SIDE_TO_TRAIT_MAP[side])
+        else:
+            if self.y_axis:
+                if (self.y_axis.title and self.y_axis.orientation == side):
+                    return 80
+            if self.x_axis:
+                if (self.x_axis.title and self.x_axis.orientation == side):
+                    return 80
+            return 50
+
+    def _get_padding_top(self):
+        return self._find_padding("top")
+
+    def _get_padding_bottom(self):
+        return self._find_padding("bottom")
+
+    def _get_padding_left(self):
+        return self._find_padding("left")
+
+    def _get_padding_right(self):
+        return self._find_padding("right")
+
+    def _set_padding_top(self, value):
+        self._padding_top = value
+
+    def _set_padding_bottom(self, value):
+        self._padding_bottom = value
+
+    def _set_padding_left(self, value):
+        self._padding_left = value
+
+    def _set_padding_right(self, value):
+        self._padding_right = value
 
     border_visible = True
 


Test patch:
diff --git a/chaco/tests/test_data_view.py b/chaco/tests/test_data_view.py
--- a/chaco/tests/test_data_view.py
+++ b/chaco/tests/test_data_view.py
@@ -1,6 +1,37 @@
 import unittest
 
-from chaco.api import DataRange2D, DataView, GridDataSource
+from numpy import linspace, sin
+
+from traits.api import Enum, HasTraits, Instance
+from traitsui.api import UItem, View
+from chaco.api import (
+    ArrayPlotData, DataRange2D, DataView, GridDataSource, Plot
+)
+from enable.api import ComponentEditor
+
+
+class DummyPaddingPlot(HasTraits):
+    orientation = Enum("top", "bottom", "left", "right")
+    plot = Instance(Plot)
+
+    traits_view = View(
+        UItem(
+            'plot',
+            editor=ComponentEditor()
+        ),
+    )
+
+    def _plot_default(self):
+        x = linspace(0, 10, 10)
+        plotdata = ArrayPlotData(x=x, y=x)
+
+        plot = Plot(plotdata)
+        plot.plot(("x", "y"), type="line", color="blue")
+
+        plot.x_axis.title = "X AXIS"
+        plot.x_axis.orientation = self.orientation
+
+        return plot
 
 
 class DataViewTestCase(unittest.TestCase):
@@ -35,3 +66,11 @@ def test_range2d_changed(self):
         self.assertTrue(old_range.sources == [])
         self.assertTrue(dv.range2d.x_range is dv.index_mapper.range)
         self.assertTrue(dv.range2d.y_range is dv.value_mapper.range)
+
+    # regression test for enthought/chaco#735
+    def test_padding_with_axis_title(self):
+        for orientation in ["top", "bottom", "left", "right"]:
+            dummy_plot = DummyPaddingPlot(orientation=orientation)
+            self.assertEqual(
+                getattr(dummy_plot.plot, "padding_" + orientation), 80
+            )