From 196edf85c7cfc653a6049a8b9e3ae88ab52ab0b7 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Sat, 19 Sep 2020 19:49:23 +0200 Subject: [PATCH 1/3] Inherit OffsetBox.{get_offset,get_window_extent}. By making the parameters of OffsetBox.get_offset optional (which matches the fact that there are currently subclasses for which get_offset does *not* take any parameters), and adjusting OffsetBox.get_window_extent to use get_extent (which is typically redefined in subclasses) instead of get_extent_offsets (which is not), we can inherit the implementation of OffsetBox.get_window_extent across most subclasses instead of having to copy-paste it again and again. --- lib/matplotlib/offsetbox.py | 70 ++++++------------------------------- 1 file changed, 11 insertions(+), 59 deletions(-) diff --git a/lib/matplotlib/offsetbox.py b/lib/matplotlib/offsetbox.py index 229158066675..e49c0d72c616 100644 --- a/lib/matplotlib/offsetbox.py +++ b/lib/matplotlib/offsetbox.py @@ -272,23 +272,15 @@ def offset(width, height, xdescent, ydescent, renderer) \ self._offset = xy self.stale = True - def get_offset(self, width, height, xdescent, ydescent, renderer): + def get_offset(self, *args, **kwargs): """ - Return the offset as a tuple (x, y). - - The extent parameters have to be provided to handle the case where the - offset is dynamically determined by a callable (see - `~.OffsetBox.set_offset`). - - Parameters - ---------- - width, height, xdescent, ydescent - Extent parameters. - renderer : `.RendererBase` subclass + Return the (x, y) offset. + Parameters must be passed if the offset is dynamically determined by a + callable (see `~.OffsetBox.set_offset`), and are forwarded to that + callable. """ - return (self._offset(width, height, xdescent, ydescent, renderer) - if callable(self._offset) + return (self._offset(*args, **kwargs) if callable(self._offset) else self._offset) def set_width(self, width): @@ -347,7 +339,7 @@ def get_extent(self, renderer): def get_window_extent(self, renderer): # docstring inherited - w, h, xd, yd, offsets = self.get_extent_offsets(renderer) + w, h, xd, yd = self.get_extent(renderer) px, py = self.get_offset(w, h, xd, yd, renderer) return mtransforms.Bbox.from_bounds(px - xd, py - yd, w, h) @@ -622,21 +614,9 @@ def set_offset(self, xy): xy : (float, float) The (x, y) coordinates of the offset in display units. """ - self._offset = xy self.offset_transform.clear() self.offset_transform.translate(xy[0], xy[1]) - self.stale = True - - def get_offset(self): - """Return offset of the container.""" - return self._offset - - def get_window_extent(self, renderer): - # docstring inherited - w, h, xd, yd = self.get_extent(renderer) - ox, oy = self.get_offset() # w, h, xd, yd) - - return mtransforms.Bbox.from_bounds(ox - xd, oy - yd, w, h) + super().set_offset(xy) def get_extent(self, renderer): """Return width, height, xdescent, ydescent of box.""" @@ -782,20 +762,9 @@ def set_offset(self, xy): xy : (float, float) The (x, y) coordinates of the offset in display units. """ - self._offset = xy self.offset_transform.clear() self.offset_transform.translate(xy[0], xy[1]) - self.stale = True - - def get_offset(self): - """Return offset of the container.""" - return self._offset - - def get_window_extent(self, renderer): - # docstring inherited - w, h, xd, yd = self.get_extent(renderer) - ox, oy = self.get_offset() - return mtransforms.Bbox.from_bounds(ox - xd, oy - yd, w, h) + super().set_offset(xy) def get_extent(self, renderer): _, h_, d_ = renderer.get_text_width_height_descent( @@ -883,20 +852,9 @@ def set_offset(self, xy): xy : (float, float) The (x, y) coordinates of the offset in display units. """ - self._offset = xy self.offset_transform.clear() self.offset_transform.translate(xy[0], xy[1]) - self.stale = True - - def get_offset(self): - """Return offset of the container.""" - return self._offset - - def get_window_extent(self, renderer): - # docstring inherited - w, h, xd, yd = self.get_extent(renderer) - ox, oy = self.get_offset() # w, h, xd, yd) - return mtransforms.Bbox.from_bounds(ox - xd, oy - yd, w, h) + super().set_offset(xy) def get_extent(self, renderer): # clear the offset transforms @@ -1077,9 +1035,7 @@ def set_bbox_to_anchor(self, bbox, transform=None): def get_window_extent(self, renderer): # docstring inherited self._update_offset_func(renderer) - w, h, xd, yd = self.get_extent(renderer) - ox, oy = self.get_offset(w, h, xd, yd, renderer) - return Bbox.from_bounds(ox - xd, oy - yd, w, h) + return super().get_window_extent(renderer) def _update_offset_func(self, renderer, fontsize=None): """ @@ -1230,10 +1186,6 @@ def set_zoom(self, zoom): def get_zoom(self): return self._zoom - def get_offset(self): - """Return offset of the container.""" - return self._offset - def get_children(self): return [self.image] From a68dfad5a9fe57fb8ecc127a4c1551279aec5aff Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Sun, 21 Nov 2021 14:12:38 +0100 Subject: [PATCH 2/3] Inline AnchoredOffsetBox._update_offset_func. It is only called in `get_window_extent` -- and in `draw`, but *that* call is immediately followed by a call to `get_window_extent`, so it is redundant. --- lib/matplotlib/offsetbox.py | 24 ++++++-------------- lib/mpl_toolkits/axes_grid1/inset_locator.py | 15 ++++-------- 2 files changed, 11 insertions(+), 28 deletions(-) diff --git a/lib/matplotlib/offsetbox.py b/lib/matplotlib/offsetbox.py index e49c0d72c616..64ded364c0a3 100644 --- a/lib/matplotlib/offsetbox.py +++ b/lib/matplotlib/offsetbox.py @@ -1034,27 +1034,19 @@ def set_bbox_to_anchor(self, bbox, transform=None): def get_window_extent(self, renderer): # docstring inherited - self._update_offset_func(renderer) - return super().get_window_extent(renderer) - - def _update_offset_func(self, renderer, fontsize=None): - """ - Update the offset func which depends on the dpi of the - renderer (because of the padding). - """ - if fontsize is None: - fontsize = renderer.points_to_pixels( - self.prop.get_size_in_points()) + # Update the offset func, which depends on the dpi of the renderer + # (because of the padding). + fontsize = renderer.points_to_pixels(self.prop.get_size_in_points()) def _offset(w, h, xd, yd, renderer): bbox = Bbox.from_bounds(0, 0, w, h) - borderpad = self.borderpad * fontsize + pad = self.borderpad * fontsize bbox_to_anchor = self.get_bbox_to_anchor() - x0, y0 = _get_anchored_bbox( - self.loc, bbox, bbox_to_anchor, borderpad) + x0, y0 = _get_anchored_bbox(self.loc, bbox, bbox_to_anchor, pad) return x0 + xd, y0 + yd self.set_offset(_offset) + return super().get_window_extent(renderer) def update_frame(self, bbox, fontsize=None): self.patch.set_bounds(bbox.bounds) @@ -1066,11 +1058,9 @@ def draw(self, renderer): if not self.get_visible(): return - fontsize = renderer.points_to_pixels(self.prop.get_size_in_points()) - self._update_offset_func(renderer, fontsize) - # update the location and size of the legend bbox = self.get_window_extent(renderer) + fontsize = renderer.points_to_pixels(self.prop.get_size_in_points()) self.update_frame(bbox, fontsize) self.patch.draw(renderer) diff --git a/lib/mpl_toolkits/axes_grid1/inset_locator.py b/lib/mpl_toolkits/axes_grid1/inset_locator.py index 56e3b83573b1..6b9933e353c1 100644 --- a/lib/mpl_toolkits/axes_grid1/inset_locator.py +++ b/lib/mpl_toolkits/axes_grid1/inset_locator.py @@ -70,18 +70,11 @@ def draw(self, renderer): def __call__(self, ax, renderer): self.axes = ax - - fontsize = renderer.points_to_pixels(self.prop.get_size_in_points()) - self._update_offset_func(renderer, fontsize) - - width, height, xdescent, ydescent = self.get_extent(renderer) - - px, py = self.get_offset(width, height, 0, 0, renderer) - bbox_canvas = Bbox.from_bounds(px, py, width, height) + bbox = self.get_window_extent(renderer) + px, py = self.get_offset(bbox.width, bbox.height, 0, 0, renderer) + bbox_canvas = Bbox.from_bounds(px, py, bbox.width, bbox.height) tr = ax.figure.transFigure.inverted() - bb = TransformedBbox(bbox_canvas, tr) - - return bb + return TransformedBbox(bbox_canvas, tr) class AnchoredSizeLocator(AnchoredLocatorBase): From 35411485e9513f543ed3c375b039ab879721e1cb Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Sun, 13 Feb 2022 16:50:26 +0100 Subject: [PATCH 3/3] Make OffsetBox.set_offset callables nullary. --- lib/matplotlib/legend.py | 8 +++- lib/matplotlib/offsetbox.py | 39 +++++++++++++------- lib/mpl_toolkits/axes_grid1/inset_locator.py | 2 +- 3 files changed, 33 insertions(+), 16 deletions(-) diff --git a/lib/matplotlib/legend.py b/lib/matplotlib/legend.py index 7aef9ed7e8b3..15f56959bc4f 100644 --- a/lib/matplotlib/legend.py +++ b/lib/matplotlib/legend.py @@ -585,8 +585,12 @@ def _get_loc(self): _loc = property(_get_loc, _set_loc) - def _findoffset(self, width, height, xdescent, ydescent, renderer): + def _findoffset(self): """Helper function to locate the legend.""" + renderer = (self.figure._cachedRenderer + or self._legend_box._cached_renderer) + width, height, xdescent, ydescent = self._legend_box.get_extent( + renderer) if self._loc == 0: # "best". x, y = self._find_best_position(width, height, renderer) @@ -883,6 +887,8 @@ def get_window_extent(self, renderer=None): # docstring inherited if renderer is None: renderer = self.figure._cachedRenderer + # May not be cached on the figure, so cache it ourselves. + self._cached_renderer = renderer return self._legend_box.get_window_extent(renderer=renderer) def get_tightbbox(self, renderer): diff --git a/lib/matplotlib/offsetbox.py b/lib/matplotlib/offsetbox.py index 64ded364c0a3..deb624f8be1a 100644 --- a/lib/matplotlib/offsetbox.py +++ b/lib/matplotlib/offsetbox.py @@ -263,11 +263,19 @@ def set_offset(self, xy): xy : (float, float) or callable The (x, y) coordinates of the offset in display units. These can either be given explicitly as a tuple (x, y), or by providing a - function that converts the extent into the offset. This function - must have the signature:: + function that dynamically computes an offset (taking the arguments + passed to `.OffsetBox.get_offset`). It is recommended to make such + functions take no arguments. + + Before version 3.6, the callable had to have the signature:: def offset(width, height, xdescent, ydescent, renderer) \ -> (float, float) + + For backwards compatibility, callables with arbitrary signatures + are currently accepted as long as compatible arguments are + passed in calls to `.set_offset`. This should be considered an + implementation detail, and may be deprecated in the future. """ self._offset = xy self.stale = True @@ -276,9 +284,12 @@ def get_offset(self, *args, **kwargs): """ Return the (x, y) offset. - Parameters must be passed if the offset is dynamically determined by a - callable (see `~.OffsetBox.set_offset`), and are forwarded to that - callable. + Parameters are usually not necessary. The only exception can occur + if you have defined a callable to calculate the offset dynamically (see + `~.OffsetBox.set_offset`). It is now recommended that such a + callable does not take parameters. However, for backward-compatibility, + callables with parameters are still supported; these parameters must be + provided to `.get_offset` so that we can pass them on. """ return (self._offset(*args, **kwargs) if callable(self._offset) else self._offset) @@ -340,7 +351,9 @@ def get_extent(self, renderer): def get_window_extent(self, renderer): # docstring inherited w, h, xd, yd = self.get_extent(renderer) - px, py = self.get_offset(w, h, xd, yd, renderer) + # dynamic offset compute callables may need to access the renderer. + self._cached_renderer = renderer + px, py = self.get_offset() return mtransforms.Bbox.from_bounds(px - xd, py - yd, w, h) def draw(self, renderer): @@ -349,7 +362,7 @@ def draw(self, renderer): to the given *renderer*. """ w, h, xdescent, ydescent, offsets = self.get_extent_offsets(renderer) - px, py = self.get_offset(w, h, xdescent, ydescent, renderer) + px, py = self.get_offset() for c, (ox, oy) in zip(self.get_visible_children(), offsets): c.set_offset((px + ox, py + oy)) c.draw(renderer) @@ -530,7 +543,7 @@ def get_extent_offsets(self, renderer): def draw(self, renderer): # docstring inherited w, h, xdescent, ydescent, offsets = self.get_extent_offsets(renderer) - px, py = self.get_offset(w, h, xdescent, ydescent, renderer) + px, py = self.get_offset() for c, (ox, oy) in zip(self.get_visible_children(), offsets): c.set_offset((px + ox, py + oy)) @@ -1036,9 +1049,10 @@ def get_window_extent(self, renderer): # docstring inherited # Update the offset func, which depends on the dpi of the renderer # (because of the padding). + w, h, xd, yd = self.get_extent(renderer) fontsize = renderer.points_to_pixels(self.prop.get_size_in_points()) - def _offset(w, h, xd, yd, renderer): + def _offset(*args, **kwargs): # args are ignored; left for backcompat. bbox = Bbox.from_bounds(0, 0, w, h) pad = self.borderpad * fontsize bbox_to_anchor = self.get_bbox_to_anchor() @@ -1064,9 +1078,7 @@ def draw(self, renderer): self.update_frame(bbox, fontsize) self.patch.draw(renderer) - width, height, xdescent, ydescent = self.get_extent(renderer) - - px, py = self.get_offset(width, height, xdescent, ydescent, renderer) + px, py = self.get_offset() self.get_child().set_offset((px, py)) self.get_child().draw(renderer) @@ -1545,8 +1557,7 @@ def __init__(self, ref_artist, offsetbox, use_blit=False): def save_offset(self): offsetbox = self.offsetbox renderer = offsetbox.figure._cachedRenderer - w, h, xd, yd = offsetbox.get_extent(renderer) - offset = offsetbox.get_offset(w, h, xd, yd, renderer) + offset = offsetbox.get_offset() self.offsetbox_x, self.offsetbox_y = offset self.offsetbox.set_offset(offset) diff --git a/lib/mpl_toolkits/axes_grid1/inset_locator.py b/lib/mpl_toolkits/axes_grid1/inset_locator.py index 6b9933e353c1..a67126b37a22 100644 --- a/lib/mpl_toolkits/axes_grid1/inset_locator.py +++ b/lib/mpl_toolkits/axes_grid1/inset_locator.py @@ -71,7 +71,7 @@ def draw(self, renderer): def __call__(self, ax, renderer): self.axes = ax bbox = self.get_window_extent(renderer) - px, py = self.get_offset(bbox.width, bbox.height, 0, 0, renderer) + px, py = self.get_offset() bbox_canvas = Bbox.from_bounds(px, py, bbox.width, bbox.height) tr = ax.figure.transFigure.inverted() return TransformedBbox(bbox_canvas, tr) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy