Skip to content

[Bug]: fig.set_dpi() and figure.dpi option in matplotlibrc file not working #24644

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

Open
BearBearCodes opened this issue Dec 6, 2022 · 11 comments

Comments

@BearBearCodes
Copy link

BearBearCodes commented Dec 6, 2022

Bug summary

  • fig.set_dpi(150) only changes the dpi of plot shown in the Jupyter notebook but not in the saved figure
  • figure.dpi in the matplotlibrc file does not work at all (EDIT: bug occurs in matplotlib-inline 0.1.3 but this is fixed in matplotlib-inline 0.1.6)
  • plt.subplots(dpi=150) works as expected and changes the dpi of both the plot shown in the Jupyter notebook as well as in the saved figure

Continuation of discussion in #11227

Code for reproduction

import matplotlib as mpl
import matplotlib.pyplot as plt

fig, ax = plt.subplots()
fig.set_dpi(150)
ax.plot([1, 2], [1, 2])
print(fig.get_dpi())
print(mpl.rcParams["figure.dpi"])
print(mpl.rcParams["savefig.dpi"])
fig.savefig("test.png", dpi="figure")
plt.show()

Actual outcome

Inside a Jupyter notebook running python 3.10.4 inside a conda environment (no matplotlibrc file):

150
72.0
figure

image
Saved plot is at 72.0 dpi.

Inside a standalone Python script running the same python version inside a conda environment (no matplotlibrc file):

150
100.0
figure

Saved plot is at 100.0 dpi.

Expected outcome

  • fig.set_dpi(150) would set the dpi to 150 for the figure shown in the Jupyter notebook + the figure shown in the matplotlib window that pops up when plt.show() is called as well as the figure saved.

  • figure.dpi in the matplotlibrc file would change the default dpi of figures created (but it does not). From my original comment in #11227:

    Moreover, I'm finding that on my machine, the figure.dpi rcParam is not respected after I have modified it in my matplotlibrc file (located at ~/.config/matplotlib/matplotlibrc). Other changes to options like axes.prop_cycle in my matplotlibrc file are being reflected in the plots shown in the Jupyter notebook as well as in those saved via fig.savefig(), but changes to the figure.dpi option are not reflected in the plots shown/saved

  • The default dpi should be 100.0 instead of 72.0 (only seems to happen inside Jupyter notebooks and not in standalone python scripts)

  • (similar to point #1) fig.savefig("test.png", dpi="figure") should reflect the changed dpi that was set with fig.set_dpi(150). Instead, it saves the figure at 72.0 dpi (when running the code in a Jupyter notebook) or 100.0 dpi (when running the code in a standalone Python script). Both the Jupyter notebook and Python script are running the same Python version in the same conda environment.

Additional information

Previous discussion in #11227

Operating system

WSL2 running Ubuntu 20.04.5 LTS

Matplotlib Version

3.5.2

Matplotlib Backend

Inside Jupyter notebook (same regardless whether or not matplitlibrc file exists): module://matplotlib_inline.backend_inline.

Inside Python script with no matplotlibrc file: QtAgg

Inside Python script with matplotlibrc file: TkAgg (since I set my backend to TkAgg in my matplotlibrc file)

Python version

3.10.4

Jupyter version

6.5.2

Installation

conda 22.9.0

@tacaswell
Copy link
Member

Do you have a high-dpi screen?

@BearBearCodes
Copy link
Author

Yes I sometimes use a high dpi screen but I've noticed this behaviour even when I'm only using my laptop screen (1920×1080) with no external displays connected.

@tacaswell
Copy link
Member

The source of the problem is this:

  • figures know what their DPI is
  • this dpi is part of the transform stack and going from our various internal coordinate systems out to the final screen units (either pixel or points depending on the target format)
  • for high-dpi displays we double the resolution behind the scenes so that things look good on the screen [1]
  • to make sure that doubling does not propagate out we stash a copy of the initial dpi [2]
  • and use that cached value in the case of dpi='figure' [3]
  • however we do not update that cached value if the user changes it

Doing fig, ax = plt.subplots(dpi=150) works as expected (because we set it early). mpl.rcParams['figure.dpi'] = 150 should work, but you may have to update matplotlib-inline to get a version that does not change rcParams on import for it to work set via matplotlibrc.

[1]

# In cases with mixed resolution displays, we need to be careful if the
# device pixel ratio changes - in this case we need to resize the
# canvas accordingly. Some backends provide events that indicate a
# change in DPI, but those that don't will update this before drawing.
dpi = ratio * self.figure._original_dpi
self.figure._set_dpi(dpi, forward=False)
self._device_pixel_ratio = ratio

[2]
figure._original_dpi = figure.dpi

[3]
figure._original_dpi = figure.dpi

So we need at least one more go-around to get this correct. I am going to see what it looks like to:

  • move _original_dpi into Figure
  • make public facing ways to change the dpi update this
  • make sure we never use the public API internally

@tacaswell tacaswell added this to the v3.7.0 milestone Dec 7, 2022
@tacaswell
Copy link
Member

tagging as 3.7 as the amount of changes I am planning I expect to be bigger than what I would like to backport, but can be talked out of that (and probably should wait to see the code before jumping to conclusions anyway).

@QuLogic
Copy link
Member

QuLogic commented Dec 8, 2022

Oh, I thought I saw the set_dpi issue before, maybe I didn't fix it? I expect this would be fixed by my refactor of the HiDPI handling, but I've not touched that in some time.

@BearBearCodes
Copy link
Author

mpl.rcParams['figure.dpi'] = 150 should work, but you may have to update matplotlib-inline to get a version that does not change rcParams on import for it to work set via matplotlibrc

Upon updating matplotlib-inline from v0.1.3 to v0.1.6, I can confirm that changing the figure.dpi option in matplotlibrc works now. Also, the default dpi in a Jupyter notebook is now 100, which is the correct behaviour (previously it was 72 dpi inside a Jupyter notebook with matplotlib-inline 0.1.3).

Unfortunately, fig.set_dpi(150) still does not change the dpi of the saved figure, so the saved figure is still at 100 dpi.

Here is the output of the code snippet in my original issue after updating to matplotlib-inline 0.1.6 (with no matplotlibrc file):
image

@tacaswell
Copy link
Member

As an update, I took at pass at this and failed. @QuLogic has a plan to fix this at a lower level.

@jklymak
Copy link
Member

jklymak commented Dec 9, 2022

Just to clarify this a bit, because I think its being muddied by hiDPI and inline issues:

dpi set on creation:

import matplotlib.pyplot as plt

fig, ax = plt.subplots(dpi=150)
ax.plot([1, 2], [1, 2])
plt.show()
fig.savefig('Boo.png', dpi='figure')

Boo.png has 150 dpi

dpi set after creation:

fig, ax = plt.subplots()
fig.set_dpi(150)
ax.plot([1, 2], [1, 2])
plt.show()
fig.savefig('Boo.png', dpi='figure')

Boo.png has the dpi of 100.

No doubt because

dpi = getattr(self.figure, '_original_dpi', self.figure.dpi)
sets the printing dpi to fig._original_dpi.

@jklymak
Copy link
Member

jklymak commented Dec 9, 2022

Note that if we just modify set_dpi to change _original_dpi that fixes the problem here.

It doesn't fix the original issue #11227, which is somewhat orthogonal.

@jklymak
Copy link
Member

jklymak commented Dec 9, 2022

So far as I can tell, set_dpi is completely untested. I'd actually propose deprecating it, as there is not much reason to call it.

@tacaswell
Copy link
Member

Figure.set_dpi works correctly if you have a figure open interactively, this is a bug we should fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants