Skip to content

FIX: fix figure.set_dpi when pixel ratio not 1 #11232

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 1 commit 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
4 changes: 2 additions & 2 deletions lib/matplotlib/backends/backend_mixed.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ def start_rasterizing(self):
"""

# change the dpi of the figure temporarily.
self.figure.set_dpi(self.dpi)
self.figure.dpi = self.dpi

if self._bbox_inches_restore: # when tight bbox is used
r = process_figure_for_rasterizing(self.figure,
Expand Down Expand Up @@ -141,7 +141,7 @@ def stop_rasterizing(self):
self._rasterizing = False

# restore the figure dpi.
self.figure.set_dpi(self._figdpi)
self.figure.dpi = self._figdpi

if self._bbox_inches_restore: # when tight bbox is used
r = process_figure_for_rasterizing(self.figure,
Expand Down
2 changes: 1 addition & 1 deletion lib/matplotlib/backends/backend_pdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -2559,7 +2559,7 @@ def print_pdf(self, filename, *,
dpi=72, # dpi to use for images
bbox_inches_restore=None, metadata=None,
**kwargs):
self.figure.set_dpi(72) # there are 72 pdf points to an inch
self.figure.dpi = 72 # there are 72 pdf points to an inch
width, height = self.figure.get_size_inches()
if isinstance(filename, PdfPages):
file = filename._file
Expand Down
2 changes: 1 addition & 1 deletion lib/matplotlib/backends/backend_ps.py
Original file line number Diff line number Diff line change
Expand Up @@ -948,7 +948,7 @@ def _print_ps(self, outfile, format, *args,
elif orientation == 'portrait': isLandscape = False
else: raise RuntimeError('Orientation must be "portrait" or "landscape"')

self.figure.set_dpi(72) # Override the dpi kwarg
self.figure.dpi = 72 # Override the dpi kwarg

if rcParams['text.usetex']:
self._print_figure_tex(outfile, format, dpi, facecolor, edgecolor,
Expand Down
1 change: 0 additions & 1 deletion lib/matplotlib/backends/backend_qt5.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,6 @@ def __init__(self, figure):
self.figure = figure
# We don't want to scale up the figure DPI more than once.
# Note, we don't handle a signal for changing DPI yet.
figure._original_dpi = figure.dpi
self._update_figure_dpi()
# In cases with mixed resolution displays, we need to be careful if the
# dpi_ratio changes - in this case we need to resize the canvas
Expand Down
2 changes: 1 addition & 1 deletion lib/matplotlib/backends/backend_svg.py
Original file line number Diff line number Diff line change
Expand Up @@ -1214,7 +1214,7 @@ def print_svgz(self, filename, *args, **kwargs):

def _print_svg(
self, filename, fh, *, dpi=72, bbox_inches_restore=None, **kwargs):
self.figure.set_dpi(72.0)
self.figure.dpi = 72.0
width, height = self.figure.get_size_inches()
w, h = width * 72, height * 72

Expand Down
42 changes: 37 additions & 5 deletions lib/matplotlib/figure.py
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,7 @@ def __init__(self,
self.dpi_scale_trans = Affine2D().scale(dpi, dpi)
# do not use property as it will trigger
self._dpi = dpi
self._original_dpi = dpi
self.bbox = TransformedBbox(self.bbox_inches, self.dpi_scale_trans)

self.frameon = frameon
Expand Down Expand Up @@ -964,13 +965,44 @@ def set_facecolor(self, color):
"""
self.patch.set_facecolor(color)

def set_dpi(self, val):
def set_dpi(self, dpi):
Copy link
Member

Choose a reason for hiding this comment

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

Please don't change argument names, it will break people doing **kwargs into it.

"""
Set the resolution of the figure in dots-per-inch.
Set the nominal resolution of the figure in dots-per-inch.

.. ACCEPTS: float
"""
self.dpi = val
This is nominal because some screens have "hi-dpi" or "Retina",
where pixels are doubled. This dpi is the non-doubled value.

Setting the dpi different from your screen dpi makes the figure larger
or smaller on the screen than the width and height specified in
`~.Figure.get_width_height`.

Parameters
----------
dpi : float

Notes
-----
Some backends have the concept of "hi-dpi" displays (or "Retina")
where the resolution is doubled, but dimensions are traditionally
specified as if the resolution were not doubled. Some Matplotlib
GUI backends respect this doubling (i.e. Qt5Agg), but Matplotlib still
specifies dimensions and dpi to matplotlib as if the resolution
were not doubled. The default figure
value is nominally 100 dpi, but displays that are deignated "hi-dpi"
will internally double this to 200 dpi. We keep the "nominal" dpi
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 not quite true, we also need to save the original to drop back to the correct dpi for savefig with dpi='figure'.

because some computer set ups have one display at normal dpi, and a
second at hi-dpi, so a nominal resolution must be stored to stop
figures from doubling or halving is size when moved between displays.
"""

# some backends set self._dpi to be a ratio times
# self._original_dpi:
ratio = self._dpi / self._original_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 am not sure that we want to rely on this here. I can not produce one off the top of my head, but this feels like it is prone to race conditions.

This dpi doubling is the business of the GUI backends and I would like to keep as much of it there as possible. What we should do is set the _original_dpi and then let the GUI know and have it sort the doubling out.

Copy link
Member Author

Choose a reason for hiding this comment

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

Well race conditions can maybe be cured by clamping down the API a bit. See discussion above.

The figure object needs to know about the dpi doubling because thats how all the drawing gets done.

We could temporarily set the dpi to twice at draw time.... Hmmmm. let me look into that....

Copy link
Member Author

Choose a reason for hiding this comment

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

Oh, no its not that simple because of interactivity. I think it really is better to actually set the dpi for the figure object so it knows its actual rendered dpi....


# _original_dpi is the nominal dpi before any hi-dpi changes.
self._original_dpi = dpi
# calls _set_dpi with the actual display dpi...
self.dpi = dpi * ratio
self.stale = True

def set_figwidth(self, val, forward=True):
Expand Down