| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
yes exhibit A as to why setting the figure dpi before rendering make our lives a lot harder. I had similar bug reports 6 months ago.
Apparently they still haven't been solved? Are they duplicates?
Some of this is due to the high-dpi support for Qt which effectively renders the figure at double resolution for the screen (this is due to the way that GUI frameworks deal with high-dpi, they default to using a 2x2 pixel grid for your images unless you explicitly ask it to give you access to the screen resolution). To keep the figure the correct size on the screen and the fonts at the expected size. There are a number of some what fragile latches to keep track of the 'real' and 'screen' DPI and to cope with the user moving the window from a low to a high dpi screen etc. Some of these rely on callbacks from the Qt side to fire to work correctly (see the comments in https://github.com/matplotlib/matplotlib/blob/master/lib/matplotlib/tests/test_backend_qt5.py#L94 ).
You are running these as python script.py? I suspect running these interactively in IPython would give different results (due to the Qt event loop running in the background).
The Tk one looks like a bug in the Tk window resize code (we need to set the window to be the size of the plot + the size of the toolbar, looks like we are missing the correction is the case of changing the DPI)
I tested everything via python script.py. But I observe the same when running them in Spyder via the IPython Qt console (which is how I usually work with python); I suppose that would be the same as typing runfile(script.py) into IPyton. Does that qualify as "interactively" or do you mean something else?
The high-res stuff might explain the problems of Bug 2 I suppose. But if there is no figure shown at all, none of this should take any effect, right?
For figure:
dpi : [ *None* | scalar > 0 | 'figure' ]
The resolution in dots per inch. If *None*, defaults to
:rc:`savefig.dpi`. If 'figure', uses the figure's dpi value.
So I think if you don't specify the dpi it'll fall back to the default, so that is your first case. Why it does something different for tk is beyond me...
EDIT oops sorry I see you specified dpi=figure. Well that is mysterious.
For plt.show the question is what do we expect to see if you have set the figure dpi to something different than your screen resolution?
Uh, so there is even another bug? Because the first two figures are identical. I thought this to be expected because I assumed the default to be "figure", but if it isn't then the first figure should have a dpi of 100 instead of 200.
But you may simply plug dpi="figure" into the first savefig command; the complete bug report stays valid.
No, the first two are correct because for the first one you are setting the figure DPI to 200 (which will render to the screen at 400) and save at 200, in the second case you are telling savefig to use 200 DPI (which it is). The third case is exposing a race condition someplace between the figure state and the GUI state.
What the heck does "screen resolution" mean here? I'm not measuring anything with a ruler on my screen. But just because you are asking, I have a 1920x1080 pixel monitor with a diagonal of 27 inch, hence my screen resolution is sqrt(1920^2+1080^2)/27. = 82 ppi.
Nice to know - but completely unrelated to the bug report.
figure DPI to 200 (which will render to the screen at 400) and save at 200
This does not make any sense to me at all. Where is the number 400 coming from? And why should it save at 200 dpi, if - as the documentation states - the default rcParam is used, which is 100 which is "figure"?
But as I said, feel free to replace the respective line by fig1.savefig("fig1.png", dpi="figure"), the complete bug report stays valid.
Again I ask. What is the point of setting the figure dpi before print time? It’s not documented so far as I can see
OK, so on hi-dpi my machine using Qt5Agg
import matplotlib
#matplotlib.use('tkagg')
import matplotlib.pyplot as plt
plt.close("all")
fig1, ax1 = plt.subplots(num="expected", dpi=100)
ax1.plot([1,2])
fig3, ax3 = plt.subplots(num="buggy", dpi=50)
fig3.set_dpi(100)
plt.show()
print(fig3.get_dpi())prints 100 but yields the original 50 dpi
If I change the original dpi to 30:
fig3, ax3 = plt.subplots(num="buggy", dpi=30)
So it makes the window 50% the size of the default window in both cases, and then makes the lines thinner according to the original figure dpi.
If I do
fig3, ax3 = plt.subplots(num="buggy", dpi=30)
fig3.set_dpi(150)I get the bigger window, but still at the fuzzier resolution.
The high-dpi GUI frameworks need a double-resolution image for painting to the screen. It is rather annoying, but it is the way it is (you can dig up quite a few comments from me about a year ago complaining vociferously about this, but I have moved on to acceptance).
The default value of `savefig.dpi' is 'figure'.
matplotlib/lib/matplotlib/figure.py
Line 1852 in 9ec4b95
| kwargs.setdefault('dpi', rcParams['savefig.dpi']) |
matplotlib/lib/matplotlib/rcsetup.py
Line 1347 in 9ec4b95
| 'savefig.dpi': ['figure', validate_dpi], # DPI |
which eventually falls through to backend bases
matplotlib/lib/matplotlib/backend_bases.py
Lines 2147 to 2148 in 9ec4b95
| if dpi == 'figure': | |
| dpi = getattr(self.figure, '_original_dpi', self.figure.dpi) |
to resolve the string to a number. Note that this looks for an optional _original_dpi attribute on the figure which is set
| figure._original_dpi = figure.dpi |
and similarly in webagg. However it is not updated when set_dpi is called which is likely the source of the bug.
@jklymak Either we set the dpi at the figure level and fold it into the transform stack or we would have to pass the dpi into every single backend draw method. We also use the dpi + figure size to sort out how big to make the gui windows.
I am experiencing a similar issue on matplotlib 3.5.2 with python 3.10.4 running on WSL2 with Ubuntu 20.04.5 LTS.
EDIT: I am using TkAgg as my backend
tl;dr:
More details:
When I run the following code in a Jupyter notebook running IPython 8.1.1,
fig, ax = plt.subplots()
fig.set_dpi(150)
ax.plot([1, 2], [1, 2])
print(fig.get_dpi())
fig.savefig("test.png", dpi="figure")
plt.show()I find that the Jupyter preview window reflects the increased DPI but the saved figure does NOT respect the changed dpi (it is still at the default 72 dpi).
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. Note that if I instead set the dpi via plt.subplots(dpi=150), then the plots shown/saved both have a dpi of 150, as expected.
@BearBearCodes Can you please check the value of mpl.rcParams['savefig.dpi'] ?
I do not understand what you mean by "jupyter preview window" and our default dpi has been 100 from mpl2.
Can you reproduce this issues with complete stand alone script (including imports) and no mplrcparams file?
Actually let's close this, since DPI handling in most of the backends has changed significantly. @BearBearCodes if you can open a new issue with all the required information, that would be more clear. Thanks!
Hi @tacaswell, thanks for your quick response and apologies for the confusion.
By "jupyter preview window", I just mean the output you see under the Jupyter code cell after executing the code in that cell. A screenshot of what I mean is below.

Also as you can see in the screenshot, the mpl.rcParams["savefig.dpi"] option is set to figure and the figure's dpi is set to 72. This is with no matplotlibrc file.
Interestingly, if I run the exact same code seen in the screenshot above in a python file rather than in a Jupyter notebook, the default figure dpi is 100. But if I change the dpi to, e.g., 150 using the following code in a standalone python file
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()then the saved figure is still at 100 dpi rather than 150.
Actually let's close this, since DPI handling in most of the backends has changed significantly. @BearBearCodes if you can open a new issue with all the required information, that would be more clear. Thanks!
Okay I will do that. Thank you!
Sorry for the crossed posts. Please also be careful to use the same python environment to start Jupyter and run the standalone scripts.
@jklymak yep I am using running both the Jupyter notebook and the standalone python script from a conda environment.
| Back | FazBrowse Home | New Git URL |
Bug report
Bug summary
Changing the figure's dpi via fig.set_dpi() does not have the desired effect. There are strange things happening when saving or showing the figure as detailed below.
Bug 1 - saving
In the following a figure with dpi=200 should be saved in all 3 cases.
import matplotlib.pyplot as plt get_img_size = lambda path: print(plt.imread(path).shape) fig1, ax1 = plt.subplots(dpi=200) ax1.plot([1,2]) fig1.savefig("fig1.png") get_img_size("fig1.png") fig2, ax2 = plt.subplots(dpi=100) ax2.plot([1,2]) fig2.savefig("fig2.png", dpi=200) get_img_size("fig2.png") fig3, ax3 = plt.subplots(dpi=100) ax3.plot([1,2]) fig3.set_dpi(200) fig3.savefig("fig3.png", dpi="figure") get_img_size("fig3.png")Qt5Agg
The outcome using the Qt5Agg backend of this is
i.e. in the first two cases we get the expected 200 dpi figure. In the last case, where the figure dpi is set via fig.set_dpi it seems to be ignored and the output image has a dpi of 100.
TkAgg
Using the TkAgg backend, the saved figures are as expected ((960, 1280, 4) in all cases.)
Bug 2 - showing
In the following we would expect to get two identical figures shown, both with dpi=100 and 640x480 pixels large.
plt.close("all") fig1, ax1 = plt.subplots(num="expected", dpi=100) ax1.plot([1,2]) fig3, ax3 = plt.subplots(num="buggy", dpi=50) ax3.plot([1,2]) fig3.set_dpi(100) print(fig3.get_dpi()) fig3.savefig("fig3a.png", dpi="figure") print(fig3.get_dpi()) plt.show() print(fig3.get_dpi())Qt5Agg
The output (with Qt5Agg) is the following:
The printed dpi is correctly 100 before showing the figure. After showing, it prints 50. The shown figures have both the correct size, but the figure where dpi is set via fig.set_dpi has narrower linewidth and smaller fontsize. It looks exaclty like a figure that would have been expected for dpi=50, just with the wrong pixel size.
TkAgg
Using TkAgg backend, the output is correctly printed as 100 in all cases, but the shown figures differ in size; the left (expected) figure is 640x480 pixels, while the right figure is 640x445 pixels.
Matplotlib version
Running the same with matplotlib 2.0.2 (all other versions the same) I get
Qt5Agg, saving: (960, 1280, 4), (960, 1280, 4), (480, 640, 4) - same bug as above with master.
Qt5Agg, showing 100, 100, 100 - printed dpi are all the same, shown figures are the same.
TkAgg, saving: (960, 1280, 4) in all cases. - same expected behaviour as above.
TkAgg, showing 100, 100, 100 - printed dpi are all the same, shown figures differ in same manner as above.