Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Shorten various offsetbox implementations #21699

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion lib/matplotlib/legend.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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):
Expand Down
123 changes: 38 additions & 85 deletions lib/matplotlib/offsetbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,32 +263,35 @@ 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs an API change note.

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

def get_offset(self, width, height, xdescent, ydescent, renderer):
def get_offset(self, *args, **kwargs):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is deeply confusing. It's almost impossible for the user to figure out whether he has to pass anything and if so what. We can't have that on public API. At the very least, you should make the parameters explicit

Suggested change
def get_offset(self, *args, **kwargs):
def get_offset(self, width=None, height=None, xdescent=None, ydescent=None, renderer=None):

But even with that I'm very concerned with the API. "The method needs arguments depending on the subclass and/or some configurable state." is not a contract you can program against. It's bad enough that we currently have the inconsistency that some subclasses do not take arguments. But at least that's explicit and when you know the class, you know what to pass. This change here would make that more implicit.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still propose to leave the parameters explicit until they are removed completely.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way the API currently is is just as bad though. In some cases you have to pass arguments .... just for us to drop them on the floor!

I think that this is going the right direction (no inputs anywhere), but I think I agree we need more steps to get there.

"""
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 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(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):
Expand Down Expand Up @@ -347,8 +350,10 @@ def get_extent(self, renderer):

def get_window_extent(self, renderer):
# docstring inherited
w, h, xd, yd, offsets = self.get_extent_offsets(renderer)
px, py = self.get_offset(w, h, xd, yd, renderer)
w, h, xd, yd = self.get_extent(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):
Expand All @@ -357,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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any user who has used set_offset with a callable will be broken by this (as they no longer get the inputs they expect).

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)
Expand Down Expand Up @@ -538,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))

Expand Down Expand Up @@ -622,21 +627,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."""
Expand Down Expand Up @@ -782,20 +775,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(
Expand Down Expand Up @@ -883,20 +865,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
Expand Down Expand Up @@ -1076,29 +1047,20 @@ def set_bbox_to_anchor(self, bbox, transform=None):

def get_window_extent(self, renderer):
# docstring inherited
self._update_offset_func(renderer)
# Update the offset func, which depends on the dpi of the renderer
# (because of the padding).
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)

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())
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)
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)
Expand All @@ -1110,17 +1072,13 @@ 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)

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)
Expand Down Expand Up @@ -1230,10 +1188,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]

Expand Down Expand Up @@ -1603,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)

Expand Down
15 changes: 4 additions & 11 deletions lib/mpl_toolkits/axes_grid1/inset_locator.py
Original file line number Diff line number Diff line change
Expand Up @@ -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_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):
Expand Down