1

Before plotting using matplotlib, you must specify your display's DPI if you have a high DPI display, since otherwise the image is too small. I have a 4K display, so I definitely need to do this. (I think that matplotlib should automatically do this for you, but that is another topic...)

As a first attempt to specify the DPI, consider the code below. It manually specifies the display's DPI and then creates and plots a test DataFrame:

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import sys

# method #1: manually specify my display's DPI:
dpi = 163    # this value is valid for my Dell U2718Q 4K (3840 x 2160) display
plt.rcParams["figure.dpi"] = dpi
print("plt.matplotlib.rcParams[\"figure.dpi\"] = " + str(plt.matplotlib.rcParams["figure.dpi"]))

# define a test DataFrame (here, chose to calculate sin and cos over their range of 2 pi):
n = 100
x = (2 * np.pi / n) * np.arange(n)
df = pd.DataFrame( {
        "sin(x)" : np.sin(x),
        "cos(x)": np.cos(x),
    }
)

# plot the DataFrame:
df.plot(figsize = (12, 8), title = "sin and cos", grid = True, color = ["red", "green"])

When I put the code above into a file and run it all at once in PyCharm, everything behaves exactly as expected: the script completes without error, the plot is generated at the correct size, and the plot remains open in a window after the script ends.

So far, so good.

But the code above is brittle: run it on a computer with a different display DPI, and the image will not be sized correctly.

Doing a web search, I found this link which has code claims to automatically determine your display's DPI. My (slight) adaptation of the code is this

# method #2: call code to determine my display's DPI (only works if the backend is Qt)
if plt.get_backend() == "Qt5Agg":
    from matplotlib.backends.qt_compat import QtWidgets
    qApp = QtWidgets.QApplication(sys.argv)
    plt.matplotlib.rcParams["figure.dpi"] = qApp.desktop().physicalDpiX()

If I modify my file to use the code above ("method #2") instead of the manual DPI setting ("method #1"), I find that the script completes without error, but the plot only comes up for a brief instant before being automatically closed!

By successively commenting out lines in the "method #2" code, starting with the last and working backwards, I have determined that the culprit is the call to QtWidgets.QApplication(sys.argv).

In particular, if I reduce the "method #2" code to just this

if plt.get_backend() == "Qt5Agg":
    from matplotlib.backends.qt_compat import QtWidgets
    QtWidgets.QApplication(sys.argv)

I get this plot auto close behavior.

Another defect, is that the original "method #2" code calculates the DPI of my monitor, a Dell U2718Q, to be 160, when it really is 163: in this link go to p. 3 / 4 and look at the Pixels per inch (PPI) spec.

Does anyone know of a solution to this?

Better code to determine the DPI?

A modification of the "method #2" code which will not cause plots to auto close?

Is this a bug that needs to be reported to matplotlib or Qt?

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
HaroldFinch
  • 762
  • 1
  • 6
  • 17
  • 1
    It is because you are starting a Qt application without starting a message loop. Add `sys.exit(qApp.exec_())` in the end of your script. – Jens Munk Feb 16 '21 at 17:12
  • 1
    For your fundamental issue this should be fixed in 3.4 (upcoming) https://github.com/matplotlib/matplotlib/pull/18876. You can try matplotlib devel version and see if that helps. – Jody Klymak Feb 16 '21 at 19:38
  • @Jens Munk: you are correct: if I add `sys.exit(qApp.exec_())` to the end of my entire script, then the method #2 code will not auto close the plot window anymore. – HaroldFinch Feb 16 '21 at 20:22
  • @Jens Munk: Unfortunately, adding that call is not ideal: it is a new line of code to have to remember that is only needed if I am doing this DPI setting. Furthermore, unlike my sample script above, my actual code puts that DPI calculating code in a separate file of matplotlib options settings that I open and then exec in whatever script files of mine uses matplotlib. I tried adding that call inside that file, right after I assign "figure.dpi", but that caused all of my plot windows to not come up. – HaroldFinch Feb 16 '21 at 20:25
  • @Jody Klymak: thanks for the heads up on matplotlib 3.4 maybe fixing this issue. Your link, however, is about supporting fully fractional HiDPI, which sounds like it might fix how Qt currently thinks my DPI is 160 when it is actually 163. As far as automatically detecting the DPI in the first place, so I can avoid all this code, is [issue #19123](https://github.com/matplotlib/matplotlib/pull/19123) actually the relevant link? – HaroldFinch Feb 16 '21 at 20:36
  • @HaroldFinch. I agree this is not ideal, but I hope you understand why everything is closed. You start a `Qt` application which tears down everything unless you start the message loop. You should look into calling a function giving you the information without starting a Qt application – Jens Munk Feb 16 '21 at 21:05
  • 1
    Here you can get the information without Qt. https://stackoverflow.com/questions/54271887/calculate-screen-dpi – Jens Munk Feb 16 '21 at 21:09
  • @Jens Munk: thanks for that link. Three non Qt solutions are given in the "answered Mar 18 '20 at 20:37" by "Minion Jim". His 3 line tkinter code at the top of his answer gives me a result of 144.0709010339734 which is pretty far off from the true answer of 163. His Solution 1 gives 144.0, so similar error. His Solution 2 gives "160.2 157.2 159.4" (for the horizontal, vertical, and diagonal DPI), which are far closer to 163; I am tempted to use this one. Or just wait until Matplotlib 3.4 is released. – HaroldFinch Feb 18 '21 at 04:44
  • Sound reasonable. If you really want, you can look into the Qt source code and query this information using `ctypes` and the appropriate DLLs. On linux, I use `xrandr | grep -w connected` – Jens Munk Feb 18 '21 at 21:57

0 Answers0