I am using Python to build an image-rendering app that renders numpy arrays as images. I need a way to automate the calculation screen DPI, as I am generating images of the same size on different monitors (e.g., 2x2 inches). I am in Windows 10, Python 3.7.
I am not asking how to get the monitor's screen size -- like finding out if your monitor is 3000x2000 pixels (as was solved at: How do I get monitor resolution in Python?).
Rather, I want something that calculates DPI speficially, and takes into account any scale settings the user has applied that might change the DPI of their system. E.g., I have the scale set to 250%.
Following this answer at experts-exchange (https://www.experts-exchange.com/questions/21794611/Detecting-system-DPI-settings-in-Python.html), I have tried using this:
from ctypes import windll
def get_ppi():
LOGPIXELSX = 88
LOGPIXELSY = 90
user32 = windll.user32
user32.SetProcessDPIAware()
dc = user32.GetDC(0)
pix_per_inch = windll.gdi32.GetDeviceCaps(dc, LOGPIXELSX)
print("Horizontal DPI is", windll.gdi32.GetDeviceCaps(dc, LOGPIXELSX))
print("Vertical DPI is", windll.gdi32.GetDeviceCaps(dc, LOGPIXELSY))
user32.ReleaseDC(0, dc)
return pix_per_inch
It seems to be the right ballpark, but returns a number that is too low. It returns 240, while my actual DPI is closer to 285.
I would prefer to avoid using a GUI framework like Qt for this, as I want to minimize overhead, but if that's the only way, then I will! If I do have to use a framework, I'd prefer PyQt5.
While I said I wanted to avoid it, there is one very simple way to pull this off using PyQt5. The more I think about it, the more I think this could be the best solution, as it is largely platform independent:
import sys
from PyQt5.QtWidgets import QApplication
app = QApplication(sys.argv)
screen = app.screens()[0]
dpi = screen.physicalDotsPerInch()
app.quit()
Note that app.screens()
returns a list of screens. In my case I only have one attached but you may have multiple, so be sure to be aware of which screen you need to get dpi from. And if you keep all this contained in a function, it won't clutter your namespace with PyQt junk.
Also, for more on QScreen (sc
is a QScreen object) see this doc page:
https://doc.qt.io/qt-5/qscreen.html
There's all sorts of cool stuff you can pull from it.