From 0a26da522d9ad1addfdd9d5f3fd8c75d0cbdcf98 Mon Sep 17 00:00:00 2001 From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> Date: Mon, 15 Aug 2022 21:42:58 +0200 Subject: [PATCH 1/2] Deprecate position parameter to Axes.apply_aspect() --- .../deprecations/23629-TH.rst | 3 ++ lib/matplotlib/_tight_bbox.py | 10 +++--- lib/matplotlib/axes/_axes.py | 2 +- lib/matplotlib/axes/_base.py | 33 ++++++++++++++++--- lib/matplotlib/axes/_secondary_axes.py | 6 ++-- lib/matplotlib/figure.py | 8 ++--- lib/matplotlib/tests/test_axes.py | 4 +-- lib/mpl_toolkits/axes_grid1/parasite_axes.py | 6 ++-- lib/mpl_toolkits/mplot3d/axes3d.py | 4 +-- 9 files changed, 52 insertions(+), 24 deletions(-) create mode 100644 doc/api/next_api_changes/deprecations/23629-TH.rst diff --git a/doc/api/next_api_changes/deprecations/23629-TH.rst b/doc/api/next_api_changes/deprecations/23629-TH.rst new file mode 100644 index 000000000000..70e40af0fdc1 --- /dev/null +++ b/doc/api/next_api_changes/deprecations/23629-TH.rst @@ -0,0 +1,3 @@ +The parameter *position* in ``Axes.apply_aspect()`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +... is deprecated. It is considered internal API and now public use case is known. diff --git a/lib/matplotlib/_tight_bbox.py b/lib/matplotlib/_tight_bbox.py index db72bbdff020..00fd185836ca 100644 --- a/lib/matplotlib/_tight_bbox.py +++ b/lib/matplotlib/_tight_bbox.py @@ -30,20 +30,20 @@ def adjust_bbox(fig, bbox_inches, fixed_dpi=None): current_pos = ax.get_position(original=False).frozen() ax.set_axes_locator(lambda a, r, _pos=current_pos: _pos) # override the method that enforces the aspect ratio on the Axes - if 'apply_aspect' in ax.__dict__: - old_aspect.append(ax.apply_aspect) + if '_apply_aspect' in ax.__dict__: + old_aspect.append(ax._apply_aspect) else: old_aspect.append(sentinel) - ax.apply_aspect = lambda pos=None: None + ax._apply_aspect = lambda pos=None: None def restore_bbox(): for ax, loc, aspect in zip(fig.axes, locator_list, old_aspect): ax.set_axes_locator(loc) if aspect is sentinel: # delete our no-op function which un-hides the original method - del ax.apply_aspect + del ax._apply_aspect else: - ax.apply_aspect = aspect + ax._apply_aspect = aspect fig.bbox = origBbox fig.bbox_inches = origBboxInches diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 9a2b367fb502..ddfe9bd217fb 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -410,7 +410,7 @@ def inset_axes(self, bounds, *, transform=None, zorder=5, **kwargs): inset_ax = projection_class(self.figure, bounds, zorder=zorder, **pkw) # this locator lets the axes move if in data coordinates. - # it gets called in `ax.apply_aspect() (of all places) + # it gets called in `ax._apply_aspect() (of all places) inset_ax.set_axes_locator(inset_locator) self.add_child_axes(inset_ax) diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index 980ef2f51c94..b758dc9d5daa 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -1736,7 +1736,7 @@ def set_adjustable(self, adjustable, share=False): and any(getattr(ax.get_data_ratio, "__func__", None) != _AxesBase.get_data_ratio for ax in axs)): - # Limits adjustment by apply_aspect assumes that the axes' aspect + # Limits adjustment by _apply_aspect assumes that the axes' aspect # ratio can be computed from the data limits and scales. raise ValueError("Cannot set Axes adjustable to 'datalim' for " "Axes which override 'get_data_ratio'") @@ -1870,6 +1870,7 @@ def get_data_ratio(self): ysize = max(abs(tymax - tymin), 1e-30) return ysize / xsize + @_api.delete_parameter("3.6", "position") def apply_aspect(self, position=None): """ Adjust the Axes for a specified data aspect ratio. @@ -1881,10 +1882,17 @@ def apply_aspect(self, position=None): Parameters ---------- position : None or .Bbox + If not ``None``, this defines the position of the Axes within the figure as a Bbox. See `~.Axes.get_position` for further details. + .. admonition:: Deprecated + + Changing the *position* through ``apply_aspect`` is + considered internal API. This parameter will be removed + in the future. + Notes ----- This is called automatically when each Axes is drawn. You may need @@ -1900,6 +1908,23 @@ def apply_aspect(self, position=None): matplotlib.axes.Axes.set_anchor Set the position in case of extra space. """ + # Note: This method is a thin wrapper that only exists for the purpose + # of not exposing the position parameter as public API. + self._apply_aspect(position) + + def _apply_aspect(self, position=None): + """ + Adjust the Axes for a specified data aspect ratio. + + See the docstring of the public `apply_aspect` method. + + Parameters + ---------- + position : None or .Bbox + If not ``None``, this defines the position of the + Axes within the figure as a Bbox. See `~.Axes.get_position` + for further details. + """ if position is None: position = self.get_position(original=True) @@ -2998,7 +3023,7 @@ def _update_title_position(self, renderer): for ax in self.child_axes: # Child positions must be updated first. locator = ax.get_axes_locator() - ax.apply_aspect(locator(self, renderer) if locator else None) + ax._apply_aspect(locator(self, renderer) if locator else None) top = -np.inf for ax in axs: @@ -3061,7 +3086,7 @@ def draw(self, renderer): # loop over self and child Axes... locator = self.get_axes_locator() - self.apply_aspect(locator(self, renderer) if locator else None) + self._apply_aspect(locator(self, renderer) if locator else None) artists = self.get_children() artists.remove(self.patch) @@ -4444,7 +4469,7 @@ def get_tightbbox(self, renderer=None, call_axes_locator=True, return None locator = self.get_axes_locator() - self.apply_aspect( + self._apply_aspect( locator(self, renderer) if locator and call_axes_locator else None) for axis in self._axis_map.values(): diff --git a/lib/matplotlib/axes/_secondary_axes.py b/lib/matplotlib/axes/_secondary_axes.py index 3fabf49ebb38..565c8dbfc3d7 100644 --- a/lib/matplotlib/axes/_secondary_axes.py +++ b/lib/matplotlib/axes/_secondary_axes.py @@ -136,13 +136,13 @@ def set_location(self, location, transform=None): # this locator lets the axes move in the parent axes coordinates. # so it never needs to know where the parent is explicitly in # figure coordinates. - # it gets called in ax.apply_aspect() (of all places) + # it gets called in ax._apply_aspect() (of all places) self.set_axes_locator(_TransformedBoundsLocator(bounds, transform)) - def apply_aspect(self, position=None): + def _apply_aspect(self, position=None): # docstring inherited. self._set_lims() - super().apply_aspect(position) + super()._apply_aspect(position) @_docstring.copy(Axis.set_ticks) def set_ticks(self, ticks, labels=None, *, minor=False, **kwargs): diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index 0a0ff01a2571..021efa12ab7e 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -154,7 +154,7 @@ def __init__(self, **kwargs): self.set(**kwargs) def _get_draw_artists(self, renderer): - """Also runs apply_aspect""" + """Also runs _apply_aspect""" artists = self.get_children() artists.remove(self.patch) @@ -163,12 +163,12 @@ def _get_draw_artists(self, renderer): key=lambda artist: artist.get_zorder()) for ax in self._localaxes: locator = ax.get_axes_locator() - ax.apply_aspect(locator(ax, renderer) if locator else None) + ax._apply_aspect(locator(ax, renderer) if locator else None) for child in ax.get_children(): - if hasattr(child, 'apply_aspect'): + if hasattr(child, '_apply_aspect'): locator = child.get_axes_locator() - child.apply_aspect( + child._apply_aspect( locator(child, renderer) if locator else None) return artists diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index dd37d3d8ee80..610c13a0d71c 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -5700,7 +5700,7 @@ def test_shared_with_aspect_2(): axs[0].set_aspect(2, share=True) axs[0].plot([1, 2], [3, 4]) axs[1].plot([3, 4], [1, 2]) - plt.draw() # Trigger apply_aspect(). + plt.draw() # Trigger _apply_aspect(). assert axs[0].get_xlim() == axs[1].get_xlim() assert axs[0].get_ylim() == axs[1].get_ylim() @@ -5713,7 +5713,7 @@ def test_shared_with_aspect_3(): axs[1].set_aspect(0.5, adjustable=adjustable) axs[0].plot([1, 2], [3, 4]) axs[1].plot([3, 4], [1, 2]) - plt.draw() # Trigger apply_aspect(). + plt.draw() # Trigger _apply_aspect(). assert axs[0].get_xlim() != axs[1].get_xlim() assert axs[0].get_ylim() == axs[1].get_ylim() fig_aspect = fig.bbox_inches.height / fig.bbox_inches.width diff --git a/lib/mpl_toolkits/axes_grid1/parasite_axes.py b/lib/mpl_toolkits/axes_grid1/parasite_axes.py index 2a2b5957e844..ce4df1f0d4eb 100644 --- a/lib/mpl_toolkits/axes_grid1/parasite_axes.py +++ b/lib/mpl_toolkits/axes_grid1/parasite_axes.py @@ -126,13 +126,13 @@ def draw(self, renderer): if locator: pos = locator(self, renderer) self.set_position(pos, which="active") - self.apply_aspect(pos) + self._apply_aspect(pos) else: - self.apply_aspect() + self._apply_aspect() rect = self.get_position() for ax in self.parasites: - ax.apply_aspect(rect) + ax._apply_aspect(rect) self._children.extend(ax.get_children()) super().draw(renderer) diff --git a/lib/mpl_toolkits/mplot3d/axes3d.py b/lib/mpl_toolkits/mplot3d/axes3d.py index c7ce4ba30a8c..e50ffa79bf69 100644 --- a/lib/mpl_toolkits/mplot3d/axes3d.py +++ b/lib/mpl_toolkits/mplot3d/axes3d.py @@ -386,7 +386,7 @@ def set_box_aspect(self, aspect, *, zoom=1): self._box_aspect = self._roll_to_vertical(aspect, reverse=True) self.stale = True - def apply_aspect(self, position=None): + def _apply_aspect(self, position=None): if position is None: position = self.get_position(original=True) @@ -419,7 +419,7 @@ def draw(self, renderer): # it adjusts the view limits and the size of the bounding box # of the Axes locator = self.get_axes_locator() - self.apply_aspect(locator(self, renderer) if locator else None) + self._apply_aspect(locator(self, renderer) if locator else None) # add the projection matrix to the renderer self.M = self.get_proj() From 57c4ddf3e65331396eab6272d1de41ed8b2377a5 Mon Sep 17 00:00:00 2001 From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> Date: Mon, 3 Jun 2024 14:12:12 +0200 Subject: [PATCH 2/2] Temporary: Do we really want this? --- .../deprecations/23629-TH.rst | 4 ++-- lib/matplotlib/axes/_axes.py | 2 +- lib/matplotlib/axes/_base.py | 20 +++++++++++++++---- lib/matplotlib/axes/_secondary_axes.py | 4 ++-- 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/doc/api/next_api_changes/deprecations/23629-TH.rst b/doc/api/next_api_changes/deprecations/23629-TH.rst index 70e40af0fdc1..5b7a36436663 100644 --- a/doc/api/next_api_changes/deprecations/23629-TH.rst +++ b/doc/api/next_api_changes/deprecations/23629-TH.rst @@ -1,3 +1,3 @@ The parameter *position* in ``Axes.apply_aspect()`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -... is deprecated. It is considered internal API and now public use case is known. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +... is deprecated. It is considered internal API and no public use case is known. diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index ddfe9bd217fb..9a2b367fb502 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -410,7 +410,7 @@ def inset_axes(self, bounds, *, transform=None, zorder=5, **kwargs): inset_ax = projection_class(self.figure, bounds, zorder=zorder, **pkw) # this locator lets the axes move if in data coordinates. - # it gets called in `ax._apply_aspect() (of all places) + # it gets called in `ax.apply_aspect() (of all places) inset_ax.set_axes_locator(inset_locator) self.add_child_axes(inset_ax) diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index b758dc9d5daa..36393891f5cb 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -1736,7 +1736,7 @@ def set_adjustable(self, adjustable, share=False): and any(getattr(ax.get_data_ratio, "__func__", None) != _AxesBase.get_data_ratio for ax in axs)): - # Limits adjustment by _apply_aspect assumes that the axes' aspect + # Limits adjustment by apply_aspect assumes that the axes' aspect # ratio can be computed from the data limits and scales. raise ValueError("Cannot set Axes adjustable to 'datalim' for " "Axes which override 'get_data_ratio'") @@ -1870,7 +1870,7 @@ def get_data_ratio(self): ysize = max(abs(tymax - tymin), 1e-30) return ysize / xsize - @_api.delete_parameter("3.6", "position") + @_api.delete_parameter("3.10", "position") def apply_aspect(self, position=None): """ Adjust the Axes for a specified data aspect ratio. @@ -1882,12 +1882,11 @@ def apply_aspect(self, position=None): Parameters ---------- position : None or .Bbox - If not ``None``, this defines the position of the Axes within the figure as a Bbox. See `~.Axes.get_position` for further details. - .. admonition:: Deprecated + .. deprecated:: 3.10 Changing the *position* through ``apply_aspect`` is considered internal API. This parameter will be removed @@ -1918,6 +1917,19 @@ def _apply_aspect(self, position=None): See the docstring of the public `apply_aspect` method. + .. note:: + + It is somewhat surprising that "_apply_aspect" takes an optional + position as input, which seems more functionality than what the + name suggests. + + Generally, applying an aspect will modify the size and position + of an Axes. I haven't been able to reconstruct the history but + assume, the fact that position is updated anyway was used to + funnel additional position constraints into the already existing + code. Likely, this function should better be called + _update_geometry() nowadays. + Parameters ---------- position : None or .Bbox diff --git a/lib/matplotlib/axes/_secondary_axes.py b/lib/matplotlib/axes/_secondary_axes.py index 565c8dbfc3d7..0e66951db0c9 100644 --- a/lib/matplotlib/axes/_secondary_axes.py +++ b/lib/matplotlib/axes/_secondary_axes.py @@ -139,10 +139,10 @@ def set_location(self, location, transform=None): # it gets called in ax._apply_aspect() (of all places) self.set_axes_locator(_TransformedBoundsLocator(bounds, transform)) - def _apply_aspect(self, position=None): + def apply_aspect(self, position=None): # docstring inherited. self._set_lims() - super()._apply_aspect(position) + super().apply_aspect(position) @_docstring.copy(Axis.set_ticks) def set_ticks(self, ticks, labels=None, *, minor=False, **kwargs):