10000 DOC: add warnings about get_window_extent and BboxImage by tacaswell · Pull Request #29910 · matplotlib/matplotlib · GitHub
[go: up one dir, main page]

Skip to content

DOC: add warnings about get_window_extent and BboxImage #29910

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

Merged
merged 3 commits into from
May 16, 2025
Merged
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
25 changes: 17 additions & 8 deletions lib/matplotlib/artist.py
Original file line number Diff line number Diff line change
Expand Up @@ -329,19 +329,28 @@ def get_window_extent(self, renderer=None):
"""
Get the artist's bounding box in display space.

The bounding box' width and height are nonnegative.
The bounding box's width and height are non-negative.

Subclasses should override for inclusion in the bounding box
"tight" calculation. Default is to return an empty bounding
box at 0, 0.

Be careful when using this function, the results will not update
if the artist window extent of the artist changes. The extent
can change due to any changes in the transform stack, such as
changing the Axes limits, the figure size, or the canvas used
(as is done when saving a figure). This can lead to unexpected
behavior where interactive figures will look fine on the screen,
but will save incorrectly.
.. warning::

The extent can change due to any changes in the transform stack, such
as changing the Axes limits, the figure size, the canvas used (as is
done when saving a figure), or the DPI.

Relying on a once-retrieved window extent can lead to unexpected
behavior in various cases such as interactive figures being resized or
moved to a screen with different dpi, or figures that look fine on
screen render incorrectly when saved to file.

To get accurate results you may need to manually call
`matplotlib.figure.Figure.savefig` or
`matplotlib.figure.Figure.draw_without_rendering` to have Matplotlib
compute the rendered size.
Comment on lines +349 to +352
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 unclear to me. I feel, we are mixing two things here an it's too short to understand:

  1. draw_without_rendering ensures the window_extent is up-to-date for the current transform stack. This resolves incorrect intermediate state due to lazy evalutation, e.g. of the layout.
  2. How does savefig help here? Is this this the double save idea of Bug when saving to vector format (pdf, svg, eps) #2831 (comment). If so, that needs more explanation. Also I'm not sure on the generality of the solution Bug when saving to vector format (pdf, svg, eps) #2831 (comment).

Copy link
Member Author

Choose a reason for hiding this comment

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

I left a comment over on #2831 for why in the case of vector backends we may need savefig (tl;dr: print_pdf changes the DPI).

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 think this is too terse to be helpful and actionable for users. However, I won't block over this.

Decide yourself whether it's good enough for now or how much additional effort you want to put into this.


"""
return Bbox([[0, 0], [0, 0]])

Expand Down
56 changes: 47 additions & 9 deletions lib/matplotlib/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -1374,8 +1374,52 @@ def set_data(self, A):


class BboxImage(_ImageBase):
"""The Image class whose size is determined by the given bbox."""
"""
The Image class whose size is determined by the given bbox.

Parameters
----------
bbox : BboxBase or Callable[RendererBase, BboxBase]
The bbox or a function to generate the bbox

.. warning ::

If using `matplotlib.artist.Artist.get_window_extent` as the
callable ensure that the other artist is drawn first (lower zorder)
or you may need to renderer the figure twice to ensure that the
computed bbox is accurate.

cmap : str or `~matplotlib.colors.Colormap`, default: :rc:`image.cmap`
The Colormap instance or registered colormap name used to map scalar
data to colors.
norm : str or `~matplotlib.colors.Normalize`
Maps luminance to 0-1.
interpolation : str, default: :rc:`image.interpolation`
Supported values are 'none', 'auto', 'nearest', 'bilinear',
'bicubic', 'spline16', 'spline36', 'hanning', 'hamming', 'hermite',
'kaiser', 'quadric', 'catrom', 'gaussian', 'bessel', 'mitchell',
'sinc', 'lanczos', 'blackman'.
origin : {'upper', 'lower'}, default: :rc:`image.origin`
Place the [0, 0] index of the array in the upper left or lower left
corner of the Axes. The convention 'upper' is typically used for
matrices and images.
filternorm : bool, default: True
A parameter for the antigrain image resize filter
(see the antigrain documentation).
If filternorm is set, the filter normalizes integer values and corrects
the rounding errors. It doesn't do anything with the source floating
point values, it corrects only integers according to the rule of 1.0
which means that any sum of pixel weights must be equal to 1.0. So,
the filter function must produce a graph of the proper shape.
filterrad : float > 0, default: 4
The filter radius for filters that have a radius parameter, i.e. when
interpolation is one of: 'sinc', 'lanczos' or 'blackman'.
resample : bool, default: False
When True, use a full resampling method. When False, only resample when
the output image is larger than the input image.
**kwargs : `~matplotlib.artist.Artist` properties

"""
def __init__(self, bbox,
*,
cmap=None,
Expand All @@ -1388,12 +1432,7 @@ def __init__(self, bbox,
resample=False,
**kwargs
):
"""
cmap is a colors.Colormap instance
norm is a colors.Normalize instance to map luminance to 0-1

kwargs are an optional list of Artist keyword args
"""
super().__init__(
None,
cmap=cmap,
Expand All @@ -1409,12 +1448,11 @@ def __init__(self, bbox,
self.bbox = bbox

def get_window_extent(self, renderer=None):
if renderer is None:
renderer = self.get_figure()._get_renderer()

if isinstance(self.bbox, BboxBase):
return self.bbox
elif callable(self.bbox):
if renderer is None:
renderer = self.get_figure()._get_renderer()
return self.bbox(renderer)
else:
raise ValueError("Unknown type of bbox")
Expand Down
Loading
0