From eb5f6e471628f9605778e0717c73f3860e9f40f9 Mon Sep 17 00:00:00 2001 From: Cimarron Mittelsteadt Date: Thu, 19 Feb 2015 21:03:21 -0800 Subject: [PATCH 1/9] BUG: Account for spine position when placing the axis labels if tick labels are disabled --- lib/matplotlib/axis.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index b18c567f79cb..e28bb981a542 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -1733,7 +1733,9 @@ def _update_label_position(self, bboxes, bboxes2): x, y = self.label.get_position() if self.label_position == 'bottom': if not len(bboxes): - bottom = self.axes.bbox.ymin + bottom = (self.axes.spines['bottom'].get_transform(). + transform_path(self.axes.spines['bottom']. + get_path()).get_extents().ymin) else: bbox = mtransforms.Bbox.union(bboxes) bottom = bbox.y0 @@ -1743,7 +1745,9 @@ def _update_label_position(self, bboxes, bboxes2): else: if not len(bboxes2): - top = self.axes.bbox.ymax + top = (self.axes.spines['top'].get_transform(). + transform_path(self.axes.spines['top']. + get_path()).get_extents().ymax) else: bbox = mtransforms.Bbox.union(bboxes2) top = bbox.y1 @@ -2039,7 +2043,9 @@ def _update_label_position(self, bboxes, bboxes2): x, y = self.label.get_position() if self.label_position == 'left': if not len(bboxes): - left = self.axes.bbox.xmin + left = (self.axes.spines['left'].get_transform(). + transform_path(self.axes.spines['left']. + get_path()).get_extents().xmin) else: bbox = mtransforms.Bbox.union(bboxes) left = bbox.x0 @@ -2050,7 +2056,9 @@ def _update_label_position(self, bboxes, bboxes2): else: if not len(bboxes2): - right = self.axes.bbox.xmax + right = (self.axes.spines['right'].get_transform(). + transform_path(self.axes.spines['right']. + get_path()).get_extents().xmax) else: bbox = mtransforms.Bbox.union(bboxes2) right = bbox.x1 From 830d9ad8b1173b0396f546e1eb11d0316eef8a51 Mon Sep 17 00:00:00 2001 From: Cimarron Mittelsteadt Date: Thu, 19 Feb 2015 21:03:31 -0800 Subject: [PATCH 2/9] STY: pep8 fixes --- lib/matplotlib/axis.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index e28bb981a542..988e2b439ace 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -2154,8 +2154,8 @@ def get_ticks_position(self): majt = self.majorTicks[0] mT = self.minorTicks[0] - majorRight = ((not majt.tick1On) and majt.tick2On - and (not majt.label1On) and majt.label2On) + majorRight = ((not majt.tick1On) and majt.tick2On and + (not majt.label1On) and majt.label2On) minorRight = ((not mT.tick1On) and mT.tick2On and (not mT.label1On) and mT.label2On) if majorRight and minorRight: From 97bbf59ce875848e89be7e39605d49d7d55503bd Mon Sep 17 00:00:00 2001 From: Cimarron Mittelsteadt Date: Fri, 20 Feb 2015 08:57:47 -0800 Subject: [PATCH 3/9] DOC: Modified doctstring of _update_label_position for new functionality --- lib/matplotlib/axis.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index 988e2b439ace..29c05ea5a86a 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -1570,7 +1570,8 @@ def set_ticks(self, ticks, minor=False): def _update_label_position(self, bboxes, bboxes2): """ Update the label position based on the sequence of bounding - boxes of all the ticklabels + boxes of all the ticklabels or using the axis spines if no + ticklabels are present """ raise NotImplementedError('Derived must override') @@ -1726,7 +1727,8 @@ def set_label_position(self, position): def _update_label_position(self, bboxes, bboxes2): """ Update the label position based on the sequence of bounding - boxes of all the ticklabels + boxes of all the ticklabels or using the axis spines if no + ticklabels are present """ if not self._autolabelpos: return @@ -2036,7 +2038,8 @@ def set_label_position(self, position): def _update_label_position(self, bboxes, bboxes2): """ Update the label position based on the sequence of bounding - boxes of all the ticklabels + boxes of all the ticklabels or using the axis spines if no + ticklabels are present """ if not self._autolabelpos: return From c3267440b3a1254c3d83b45074cf8c4dd8d3dcfb Mon Sep 17 00:00:00 2001 From: Cimarron Mittelsteadt Date: Fri, 20 Feb 2015 22:35:32 -0800 Subject: [PATCH 4/9] Simplified logic in _update_label_position to use the bounding box of all ticklabels and axis spine for use in label placement --- lib/matplotlib/axis.py | 63 ++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 36 deletions(-) diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index 29c05ea5a86a..e8234d99138e 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -1569,9 +1569,8 @@ def set_ticks(self, ticks, minor=False): def _update_label_position(self, bboxes, bboxes2): """ - Update the label position based on the sequence of bounding - boxes of all the ticklabels or using the axis spines if no - ticklabels are present + Update the label position based on the the bounding box enclosing + all the ticklabels and axis spine """ raise NotImplementedError('Derived must override') @@ -1726,33 +1725,30 @@ def set_label_position(self, position): def _update_label_position(self, bboxes, bboxes2): """ - Update the label position based on the sequence of bounding - boxes of all the ticklabels or using the axis spines if no - ticklabels are present + Update the label position based on the the bounding box enclosing + all the ticklabels and axis spine """ if not self._autolabelpos: return x, y = self.label.get_position() if self.label_position == 'bottom': - if not len(bboxes): - bottom = (self.axes.spines['bottom'].get_transform(). - transform_path(self.axes.spines['bottom']. - get_path()).get_extents().ymin) - else: - bbox = mtransforms.Bbox.union(bboxes) - bottom = bbox.y0 + spinebbox = (self.axes.spines['bottom'].get_transform(). + transform_path(self.axes.spines['bottom']. + get_path()).get_extents()) + bbox = mtransforms.Bbox.union(bboxes + [spinebbox]) + bottom = bbox.y0 + self.label.set_position( (x, bottom - self.labelpad * self.figure.dpi / 72.0) ) else: - if not len(bboxes2): - top = (self.axes.spines['top'].get_transform(). - transform_path(self.axes.spines['top']. - get_path()).get_extents().ymax) - else: - bbox = mtransforms.Bbox.union(bboxes2) - top = bbox.y1 + spinebbox = (self.axes.spines['top'].get_transform(). + transform_path(self.axes.spines['top']. + get_path()).get_extents()) + bbox = mtransforms.Bbox.union(bboxes2 + [spinebbox]) + top = bbox.y1 + self.label.set_position( (x, top + self.labelpad * self.figure.dpi / 72.0) ) @@ -2037,34 +2033,29 @@ def set_label_position(self, position): def _update_label_position(self, bboxes, bboxes2): """ - Update the label position based on the sequence of bounding - boxes of all the ticklabels or using the axis spines if no - ticklabels are present + Update the label position based on the the bounding box enclosing + all the ticklabels and axis spine """ if not self._autolabelpos: return x, y = self.label.get_position() if self.label_position == 'left': - if not len(bboxes): - left = (self.axes.spines['left'].get_transform(). - transform_path(self.axes.spines['left']. - get_path()).get_extents().xmin) - else: - bbox = mtransforms.Bbox.union(bboxes) - left = bbox.x0 + spinebbox = (self.axes.spines['left'].get_transform(). + transform_path(self.axes.spines['left']. + get_path()).get_extents()) + bbox = mtransforms.Bbox.union(bboxes + [spinebbox]) + left = bbox.x0 self.label.set_position( (left - self.labelpad * self.figure.dpi / 72.0, y) ) else: - if not len(bboxes2): - right = (self.axes.spines['right'].get_transform(). + spinebbox = (self.axes.spines['right'].get_transform(). transform_path(self.axes.spines['right']. - get_path()).get_extents().xmax) - else: - bbox = mtransforms.Bbox.union(bboxes2) - right = bbox.x1 + get_path()).get_extents()) + bbox = mtransforms.Bbox.union(bboxes2 + [spinebbox]) + right = bbox.x1 self.label.set_position( (right + self.labelpad * self.figure.dpi / 72.0, y) From ef683df20bd6ea0d992008f29f711035a8de8955 Mon Sep 17 00:00:00 2001 From: Cimarron Mittelsteadt Date: Wed, 4 Mar 2015 22:21:30 -0800 Subject: [PATCH 5/9] STY: Minor tweak to refactor the spine object out of the spinebbox computation --- lib/matplotlib/axis.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index e8234d99138e..e4cd5f1e8465 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -1732,9 +1732,9 @@ def _update_label_position(self, bboxes, bboxes2): return x, y = self.label.get_position() if self.label_position == 'bottom': - spinebbox = (self.axes.spines['bottom'].get_transform(). - transform_path(self.axes.spines['bottom']. - get_path()).get_extents()) + spine = self.axes.spines['bottom'] + spinebbox = spine.get_transform().transform_path( + spine.get_path()).get_extents() bbox = mtransforms.Bbox.union(bboxes + [spinebbox]) bottom = bbox.y0 @@ -1743,9 +1743,9 @@ def _update_label_position(self, bboxes, bboxes2): ) else: - spinebbox = (self.axes.spines['top'].get_transform(). - transform_path(self.axes.spines['top']. - get_path()).get_extents()) + spine = self.axes.spines['top'] + spinebbox = spine.get_transform().transform_path( + spine.get_path()).get_extents() bbox = mtransforms.Bbox.union(bboxes2 + [spinebbox]) top = bbox.y1 @@ -2040,9 +2040,9 @@ def _update_label_position(self, bboxes, bboxes2): return x, y = self.label.get_position() if self.label_position == 'left': - spinebbox = (self.axes.spines['left'].get_transform(). - transform_path(self.axes.spines['left']. - get_path()).get_extents()) + spine = self.axes.spines['left'] + spinebbox = spine.get_transform().transform_path( + spine.get_path()).get_extents() bbox = mtransforms.Bbox.union(bboxes + [spinebbox]) left = bbox.x0 @@ -2051,9 +2051,9 @@ def _update_label_position(self, bboxes, bboxes2): ) else: - spinebbox = (self.axes.spines['right'].get_transform(). - transform_path(self.axes.spines['right']. - get_path()).get_extents()) + spine = self.axes.spines['right'] + spinebbox = spine.get_transform().transform_path( + spine.get_path()).get_extents() bbox = mtransforms.Bbox.union(bboxes2 + [spinebbox]) right = bbox.x1 From ebb2e63bc22eb067dbe840f7e128fc3f29e07aac Mon Sep 17 00:00:00 2001 From: Cimarron Mittelsteadt Date: Thu, 5 Mar 2015 17:39:23 -0800 Subject: [PATCH 6/9] Handle plots with no spines or transformations with non-standard spines --- lib/matplotlib/axis.py | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index e4cd5f1e8465..97c79df79720 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -1732,9 +1732,13 @@ def _update_label_position(self, bboxes, bboxes2): return x, y = self.label.get_position() if self.label_position == 'bottom': - spine = self.axes.spines['bottom'] - spinebbox = spine.get_transform().transform_path( - spine.get_path()).get_extents() + try: + spine = self.axes.spines['bottom'] + spinebbox = spine.get_transform().transform_path( + spine.get_path()).get_extents() + except KeyError: + # use axes if spine doesn't exist + spinebbox = self.axes.bbox bbox = mtransforms.Bbox.union(bboxes + [spinebbox]) bottom = bbox.y0 @@ -1743,9 +1747,13 @@ def _update_label_position(self, bboxes, bboxes2): ) else: - spine = self.axes.spines['top'] - spinebbox = spine.get_transform().transform_path( - spine.get_path()).get_extents() + try: + spine = self.axes.spines['top'] + spinebbox = spine.get_transform().transform_path( + spine.get_path()).get_extents() + except KeyError: + # use axes if spine doesn't exist + spinebbox = self.axes.bbox bbox = mtransforms.Bbox.union(bboxes2 + [spinebbox]) top = bbox.y1 @@ -2040,9 +2048,13 @@ def _update_label_position(self, bboxes, bboxes2): return x, y = self.label.get_position() if self.label_position == 'left': - spine = self.axes.spines['left'] - spinebbox = spine.get_transform().transform_path( - spine.get_path()).get_extents() + try: + spine = self.axes.spines['left'] + spinebbox = spine.get_transform().transform_path( + spine.get_path()).get_extents() + except KeyError: + # use axes if spine doesn't exist + spinebbox = self.axes.bbox bbox = mtransforms.Bbox.union(bboxes + [spinebbox]) left = bbox.x0 @@ -2051,9 +2063,13 @@ def _update_label_position(self, bboxes, bboxes2): ) else: - spine = self.axes.spines['right'] - spinebbox = spine.get_transform().transform_path( - spine.get_path()).get_extents() + try: + spine = self.axes.spines['right'] + spinebbox = spine.get_transform().transform_path( + spine.get_path()).get_extents() + except KeyError: + # use axes if spine doesn't exist + spinebbox = self.axes.bbox bbox = mtransforms.Bbox.union(bboxes2 + [spinebbox]) right = bbox.x1 From 54abca41433f13e24883922a778cc84d0f295f42 Mon Sep 17 00:00:00 2001 From: Cimarron Mittelsteadt Date: Thu, 5 Mar 2015 19:55:55 -0800 Subject: [PATCH 7/9] TST: Added test to check that axis labels are located correctly when no tick labels are present and axis spines are offset (see issue #3715) --- lib/matplotlib/tests/test_spines.py | 38 +++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/tests/test_spines.py b/lib/matplotlib/tests/test_spines.py index bbf149d8bb4b..a5546d36b15b 100644 --- a/lib/matplotlib/tests/test_spines.py +++ b/lib/matplotlib/tests/test_spines.py @@ -1,12 +1,15 @@ from __future__ import (absolute_import, division, print_function, unicode_literals) +import numpy as np +from nose.tools import assert_less import six -import numpy as np import matplotlib -from matplotlib.testing.decorators import image_comparison import matplotlib.pyplot as plt +import matplotlib.transforms as mtransforms +from matplotlib.testing.decorators import image_comparison, cleanup + @image_comparison(baseline_images=['spines_axes_positions']) def test_spines_axes_positions(): @@ -43,3 +46,34 @@ def test_spines_capstyle(): ax = fig.add_subplot(1,1,1) ax.set_xticks([]) ax.set_yticks([]) + + +@cleanup +def test_label_without_ticks(): + fig = plt.figure() + ax = fig.add_subplot(1, 1, 1) + plt.subplots_adjust(left=0.3, bottom=0.3) + ax.plot(np.arange(10)) + ax.yaxis.set_ticks_position('left') + ax.spines['left'].set_position(('outward', 30)) + ax.spines['right'].set_visible(False) + ax.set_ylabel('y label') + ax.xaxis.set_ticks_position('bottom') + ax.spines['bottom'].set_position(('outward', 30)) + ax.spines['top'].set_visible(False) + ax.set_xlabel('x label') + ax.xaxis.set_ticks([]) + ax.yaxis.set_ticks([]) + plt.draw() + + spine = ax.spines['left'] + spinebbox = spine.get_transform().transform_path( + spine.get_path()).get_extents() + assert_less(ax.yaxis.label.get_position()[0], spinebbox.xmin, + "Y-Axis label not left of the spine") + + spine = ax.spines['bottom'] + spinebbox = spine.get_transform().transform_path( + spine.get_path()).get_extents() + assert_less(ax.xaxis.label.get_position()[1], spinebbox.ymin, + "X-Axis label not below the spine") From 407e362015782a6a4b6aa64f2b7e89534a3701bf Mon Sep 17 00:00:00 2001 From: Cimarron Mittelsteadt Date: Thu, 5 Mar 2015 19:56:34 -0800 Subject: [PATCH 8/9] STY: pep8 fixes to test_spines --- lib/matplotlib/tests/test_spines.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/lib/matplotlib/tests/test_spines.py b/lib/matplotlib/tests/test_spines.py index a5546d36b15b..93a75e4206c4 100644 --- a/lib/matplotlib/tests/test_spines.py +++ b/lib/matplotlib/tests/test_spines.py @@ -15,35 +15,37 @@ def test_spines_axes_positions(): # SF bug 2852168 fig = plt.figure() - x = np.linspace(0,2*np.pi,100) + x = np.linspace(0, 2*np.pi, 100) y = 2*np.sin(x) - ax = fig.add_subplot(1,1,1) + ax = fig.add_subplot(1, 1, 1) ax.set_title('centered spines') - ax.plot(x,y) - ax.spines['right'].set_position(('axes',0.1)) + ax.plot(x, y) + ax.spines['right'].set_position(('axes', 0.1)) ax.yaxis.set_ticks_position('right') - ax.spines['top'].set_position(('axes',0.25)) + ax.spines['top'].set_position(('axes', 0.25)) ax.xaxis.set_ticks_position('top') ax.spines['left'].set_color('none') ax.spines['bottom'].set_color('none') + @image_comparison(baseline_images=['spines_data_positions']) def test_spines_data_positions(): fig = plt.figure() - ax = fig.add_subplot(1,1,1) + ax = fig.add_subplot(1, 1, 1) ax.spines['left'].set_position(('data', -1.5)) ax.spines['top'].set_position(('data', 0.5)) ax.spines['right'].set_position(('data', -0.5)) ax.spines['bottom'].set_position('zero') - ax.set_xlim([-2,2]) - ax.set_ylim([-2,2]) + ax.set_xlim([-2, 2]) + ax.set_ylim([-2, 2]) + @image_comparison(baseline_images=['spines_capstyle']) def test_spines_capstyle(): # issue 2542 plt.rc('axes', linewidth=20) fig = plt.figure() - ax = fig.add_subplot(1,1,1) + ax = fig.add_subplot(1, 1, 1) ax.set_xticks([]) ax.set_yticks([]) From f49a5b6b5e1ad1b33db2b3a575c1de7cae274869 Mon Sep 17 00:00:00 2001 From: Cimarron Mittelsteadt Date: Thu, 5 Mar 2015 21:41:01 -0800 Subject: [PATCH 9/9] TST: Removed test_spines.py from pep8 excluded list since it is passing pep8 now. Modified test_spines to not use assert_less since it is not available in python 2.6 --- lib/matplotlib/tests/test_coding_standards.py | 1 - lib/matplotlib/tests/test_spines.py | 8 +++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/matplotlib/tests/test_coding_standards.py b/lib/matplotlib/tests/test_coding_standards.py index 51559b782170..33a818899df9 100644 --- a/lib/matplotlib/tests/test_coding_standards.py +++ b/lib/matplotlib/tests/test_coding_standards.py @@ -213,7 +213,6 @@ def test_pep8_conformance_installed_files(): 'tests/test_mathtext.py', 'tests/test_rcparams.py', 'tests/test_simplification.py', - 'tests/test_spines.py', 'tests/test_streamplot.py', 'tests/test_subplots.py', 'tests/test_tightlayout.py', diff --git a/lib/matplotlib/tests/test_spines.py b/lib/matplotlib/tests/test_spines.py index 93a75e4206c4..b628249160ef 100644 --- a/lib/matplotlib/tests/test_spines.py +++ b/lib/matplotlib/tests/test_spines.py @@ -2,7 +2,7 @@ unicode_literals) import numpy as np -from nose.tools import assert_less +from nose.tools import assert_true import six import matplotlib @@ -71,11 +71,13 @@ def test_label_without_ticks(): spine = ax.spines['left'] spinebbox = spine.get_transform().transform_path( spine.get_path()).get_extents() - assert_less(ax.yaxis.label.get_position()[0], spinebbox.xmin, + # replace with assert_less if >python2.6 + assert_true(ax.yaxis.label.get_position()[0] < spinebbox.xmin, "Y-Axis label not left of the spine") spine = ax.spines['bottom'] spinebbox = spine.get_transform().transform_path( spine.get_path()).get_extents() - assert_less(ax.xaxis.label.get_position()[1], spinebbox.ymin, + # replace with assert_less if >python2.6 + assert_true(ax.xaxis.label.get_position()[1] < spinebbox.ymin, "X-Axis label not below the spine")