Skip to content

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

Open

Description

@BearBearCodes

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

Activity

  1. tacaswell commented on Dec 7, 2022

    @tacaswell
    Member

    Do you have a high-dpi screen?

  2. BearBearCodes commented on Dec 7, 2022

    @BearBearCodes
    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.

  3. tacaswell commented on Dec 7, 2022

    @tacaswell
    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
  4. added this to the v3.7.0 milestone on Dec 7, 2022
  5. tacaswell commented on Dec 7, 2022

    @tacaswell
    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).

  6. QuLogic commented on Dec 8, 2022

    @QuLogic
    Member

    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.

  7. BearBearCodes commented on Dec 9, 2022

    @BearBearCodes
    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

  8. tacaswell commented on Dec 9, 2022

    @tacaswell
    Member

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

  9. jklymak commented on Dec 9, 2022

    @jklymak
    Member

    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.

  10. jklymak commented on Dec 9, 2022

    @jklymak
    Member

    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.

  11. jklymak commented on Dec 9, 2022

    @jklymak
    Member

    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.

  12. modified the milestones: v3.7.0, v3.8.0 on Jan 25, 2023
  13. tacaswell commented on Aug 11, 2023

    @tacaswell
    Member

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

  14. removed this from the v3.8.0 milestone on Sep 15, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions