From a0f10b986bbd9ff9133b023a8d7173b0865fa81a Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Fri, 20 Oct 2017 21:11:59 -0700 Subject: [PATCH 1/2] ENH: Move title if x-axis is on the top of the figure --- .../title_will_not_overlap_xaxis.rst | 8 +++ examples/ticks_and_spines/tick_xlabel_top.py | 2 +- lib/matplotlib/axes/_base.py | 63 ++++++++++++++++-- .../baseline_images/test_axes/titletwiny.png | Bin 0 -> 13904 bytes lib/matplotlib/tests/test_axes.py | 36 ++++++++++ 5 files changed, 104 insertions(+), 5 deletions(-) create mode 100644 doc/users/next_whats_new/title_will_not_overlap_xaxis.rst create mode 100644 lib/matplotlib/tests/baseline_images/test_axes/titletwiny.png diff --git a/doc/users/next_whats_new/title_will_not_overlap_xaxis.rst b/doc/users/next_whats_new/title_will_not_overlap_xaxis.rst new file mode 100644 index 000000000000..cb7b971a7444 --- /dev/null +++ b/doc/users/next_whats_new/title_will_not_overlap_xaxis.rst @@ -0,0 +1,8 @@ +Axes title will no longer overlap xaxis +--------------------------------------- + +Previously the axes title had to be moved manually if an xaxis overlapped +(usually when the xaxis was put on the top of the axes). The title can +still be placed manually. However, titles that need to be moved are +at ``y=1.0``, so manualy placing at 1.0 will be moved, so if the title +is to be placed at 1.0, it should be set to something near 1.0, like 1.001. diff --git a/examples/ticks_and_spines/tick_xlabel_top.py b/examples/ticks_and_spines/tick_xlabel_top.py index db992e529b92..a437810b06a3 100644 --- a/examples/ticks_and_spines/tick_xlabel_top.py +++ b/examples/ticks_and_spines/tick_xlabel_top.py @@ -23,6 +23,6 @@ fig, ax = plt.subplots() ax.plot(x) -ax.set_title('xlabel top', pad=24) # increase padding to make room for labels +ax.set_title('xlabel top') # Note title moves to make room for ticks plt.show() diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index b771474c585b..ec20f2bc0582 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -1,5 +1,6 @@ from collections import OrderedDict import itertools +import logging import math from operator import attrgetter import types @@ -32,6 +33,8 @@ from matplotlib.rcsetup import cycler from matplotlib.rcsetup import validate_axisbelow +_log = logging.getLogger(__name__) + rcParams = matplotlib.rcParams @@ -1077,6 +1080,8 @@ def cla(self): # refactor this out so it can be called in ax.set_title if # pad argument used... self._set_title_offset_trans(title_offset_points) + # determine if the title position has been set manually: + self._autotitlepos = None for _title in (self.title, self._left_title, self._right_title): self._set_artist_props(_title) @@ -2446,6 +2451,50 @@ def handle_single_axis(scale, autoscaleon, shared_axes, interval, def _get_axis_list(self): return (self.xaxis, self.yaxis) + def _update_title_position(self, renderer): + """ + Update the title position based on the bounding box enclosing + all the ticklabels and x-axis spine and xlabel... + """ + _log.debug('update_title_pos') + + if self._autotitlepos is not None and not self._autotitlepos: + _log.debug('title position was updated manually, not adjusting') + return + + titles = (self.title, self._left_title, self._right_title) + + if self._autotitlepos is None: + for title in titles: + x, y = title.get_position() + if not np.isclose(y, 1.0): + self._autotitlepos = False + _log.debug('not adjusting title pos because title was' + ' already placed manually: %f', y) + return + self._autotitlepos = True + + for title in titles: + x, y0 = title.get_position() + y = 1.0 + # need to check all our twins too... + axs = self._twinned_axes.get_siblings(self) + + for ax in axs: + try: + if (ax.xaxis.get_label_position() == 'top' + or ax.xaxis.get_ticks_position() == 'top'): + bb = ax.xaxis.get_tightbbox(renderer) + top = bb.y1 + # we don't need to pad because the padding is already + # in __init__: titleOffsetTrans + yn = self.transAxes.inverted().transform((0., top))[1] + y = max(y, yn) + except AttributeError: + pass + + title.set_position((x, y)) + # Drawing @allow_rasterization @@ -2459,6 +2508,7 @@ def draw(self, renderer=None, inframe=False): if not self.get_visible(): return renderer.open_group('axes') + # prevent triggering call backs during the draw process self._stale = True locator = self.get_axes_locator() @@ -2479,6 +2529,8 @@ def draw(self, renderer=None, inframe=False): for spine in self.spines.values(): artists.remove(spine) + self._update_title_position(renderer) + if self.axison and not inframe: if self._axisbelow is True: self.xaxis.set_zorder(0.5) @@ -2507,6 +2559,7 @@ def draw(self, renderer=None, inframe=False): # rasterize artists with negative zorder # if the minimum zorder is negative, start rasterization rasterization_zorder = self._rasterization_zorder + if (rasterization_zorder is not None and artists and artists[0].zorder < rasterization_zorder): renderer.start_rasterizing() @@ -4051,6 +4104,12 @@ def get_tightbbox(self, renderer, call_axes_locator=True): else: self.apply_aspect() + bb_xaxis = self.xaxis.get_tightbbox(renderer) + if bb_xaxis: + bb.append(bb_xaxis) + + self._update_title_position(renderer) + bb.append(self.get_window_extent(renderer)) if self.title.get_visible(): @@ -4060,10 +4119,6 @@ def get_tightbbox(self, renderer, call_axes_locator=True): if self._right_title.get_visible(): bb.append(self._right_title.get_window_extent(renderer)) - bb_xaxis = self.xaxis.get_tightbbox(renderer) - if bb_xaxis: - bb.append(bb_xaxis) - bb_yaxis = self.yaxis.get_tightbbox(renderer) if bb_yaxis: bb.append(bb_yaxis) diff --git a/lib/matplotlib/tests/baseline_images/test_axes/titletwiny.png b/lib/matplotlib/tests/baseline_images/test_axes/titletwiny.png new file mode 100644 index 0000000000000000000000000000000000000000..670a4bebbd654cfe81af364b963ce21a2218b0d0 GIT binary patch literal 13904 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 literal 0 HcmV?d00001 diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index 5a3a3e5d4253..6eae5a7227d5 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -5411,6 +5411,42 @@ def test_axisbelow(): ax.set_axisbelow(setting) +@image_comparison(baseline_images=['titletwiny'], style='mpl20', + extensions=['png']) +def test_titletwiny(): + # Test that title is put above xlabel if xlabel at top + fig, ax = plt.subplots() + fig.subplots_adjust(top=0.8) + ax2 = ax.twiny() + ax.set_xlabel('Xlabel') + ax2.set_xlabel('Xlabel2') + ax.set_title('Title') + + +def test_titlesetpos(): + # Test that title stays put if we set it manually + fig, ax = plt.subplots() + fig.subplots_adjust(top=0.8) + ax2 = ax.twiny() + ax.set_xlabel('Xlabel') + ax2.set_xlabel('Xlabel2') + ax.set_title('Title') + pos = (0.5, 1.11) + ax.title.set_position(pos) + renderer = fig.canvas.get_renderer() + ax._update_title_position(renderer) + assert ax.title.get_position() == pos + + +def test_title_xticks_top(): + # Test that title moves if xticks on top of axes. + fig, ax = plt.subplots() + ax.xaxis.set_ticks_position('top') + ax.set_title('xlabel top') + fig.canvas.draw() + assert ax.title.get_position()[1] > 1.04 + + def test_offset_label_color(): # Tests issue 6440 fig = plt.figure() From c2db893dc6d36b3a55f53258fdc1fdbc6103b27e Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Mon, 2 Apr 2018 22:00:51 -0700 Subject: [PATCH 2/2] DOC: Whats new entry for title moving above xaxis --- .../title_will_not_overlap_xaxis.rst | 17 ++++++++++++----- lib/matplotlib/axes/_base.py | 2 +- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/doc/users/next_whats_new/title_will_not_overlap_xaxis.rst b/doc/users/next_whats_new/title_will_not_overlap_xaxis.rst index cb7b971a7444..74db251a4a5f 100644 --- a/doc/users/next_whats_new/title_will_not_overlap_xaxis.rst +++ b/doc/users/next_whats_new/title_will_not_overlap_xaxis.rst @@ -1,8 +1,15 @@ Axes title will no longer overlap xaxis --------------------------------------- -Previously the axes title had to be moved manually if an xaxis overlapped -(usually when the xaxis was put on the top of the axes). The title can -still be placed manually. However, titles that need to be moved are -at ``y=1.0``, so manualy placing at 1.0 will be moved, so if the title -is to be placed at 1.0, it should be set to something near 1.0, like 1.001. +Previously an axes title had to be moved manually if an xaxis overlapped +(usually when the xaxis was put on the top of the axes). Now, the title +will be automatically moved above the xaxis and its decorators (including +the xlabel) if they are at the top. + +If desired, the title can still be placed manually. There is a slight kludge; +the algorithm checks if the y-position of the title is 1.0 (the default), +and moves if it is. If the user places the title in the default location +(i.e. ``ax.title.set_position(0.5, 1.0)``), the title will still be moved +above the xaxis. If the user wants to avoid this, they can +specify a number that is close (i.e. ``ax.title.set_position(0.5, 1.01)``) +and the title will not be moved via this algorithm. diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index ec20f2bc0582..e49c33fe0c3e 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -2485,7 +2485,7 @@ def _update_title_position(self, renderer): if (ax.xaxis.get_label_position() == 'top' or ax.xaxis.get_ticks_position() == 'top'): bb = ax.xaxis.get_tightbbox(renderer) - top = bb.y1 + top = bb.ymax # we don't need to pad because the padding is already # in __init__: titleOffsetTrans yn = self.transAxes.inverted().transform((0., top))[1]