我正在使用 Python 构建一个图像渲染应用程序,它将 numpy 数组渲染为图像。我需要一种方法来自动计算屏幕 DPI,因为我在不同的显示器(例如,2x2 英寸)上生成相同大小的图像。我在 Windows 10,Python 3.7。
我不是在问如何获得显示器的屏幕尺寸——比如确定你的显示器是否为 3000x2000 像素(如解决:如何在 Python 中获得显示器分辨率?)。
相反,我想要一些专门计算 DPI 的东西,并考虑到用户应用的任何可能改变其系统 DPI 的比例设置。例如,我将比例设置为 250%。
按照专家交流(https://www.experts-exchange.com/questions/21794611/Detecting-system-DPI-settings-in-Python.html)的这个答案,我尝试使用这个:
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
Run Code Online (Sandbox Code Playgroud)
这似乎是正确的范围,但返回的数字太低。它返回 240,而我的实际 DPI 更接近 285。
我宁愿避免为此使用像 Qt 这样的 GUI 框架,因为我想最大限度地减少开销,但如果这是唯一的方法,那么我会的!如果我必须使用框架,我更喜欢 PyQt5。
虽然@eric的答案有效,但我宁愿避免离开标准库(忽略不包含的Python发行版ctypes)。
这最初使我想到了 ctypes,您可以在下面看到我最初的(非常复杂的)答案。最近,我找到了一种仅使用 Tk 即可完成此操作的方法(我不记得它的链接,因此将链接到具有相同内容的Stack Overflow 答案)
import tkinter
root = tkinter.Tk()
dpi = root.winfo_fpixels('1i')
Run Code Online (Sandbox Code Playgroud)
原答案
以下答案提供了两种解决方案:
第一个是 Windows 由于用户的显示缩放而报告的 DPI
第二个是通过查找显示器的物理尺寸和分辨率计算出的显示器真实 DPI
这些解决方案假设只有一台显示器,并且还设置了进程 DPI 感知(这不适合某些情况)。有关 Windows API 调用的详细信息,请参阅、和的ctypes文档。SetProcessDPIAwarenessGetDPIForWindowGetDCGetDeviceCaps
解决方案1
此解决方案不会返回正确的 DPI,而只是该显示器的 Window“缩放系数”。要获得该因子,您应该将返回的 DPI 除以 96(意思96是 的比例因子100%,而120意味着 的比例因子125%,等等)。
该方法用于tkinter获取有效值HWND(窗口的标识符),然后ctypes获取新窗口的 DPI。
# Import the libraries
import ctypes
import tkinter
# Set process DPI awareness
ctypes.windll.shcore.SetProcessDpiAwareness(1)
# Create a tkinter window
root = tkinter.Tk()
# Get the reported DPI from the window's HWND
dpi = ctypes.windll.user32.GetDpiForWindow(root.winfo_id())
# Print the DPI
print(dpi)
# Destroy the window
root.destroy()
Run Code Online (Sandbox Code Playgroud)
解决方案2
此方法应返回显示器的真实 DPI,但是,应注意两件事:
显示器的物理尺寸(偶尔)报告不正确。因此,这将导致完全不正确的 DPI 值,尽管我不确定如何防止这种情况,除了添加检查以确保它位于合理值之间(无论这意味着什么!)。
Windows 使用的分辨率通常与显示器的实际分辨率不同。此方法计算显示器的真实 DPI,但如果您想计算每物理英寸的虚拟像素数,则可以将dw和 的分配替换dh为root.winfo_screenwidth()和root.winfo_screenheight()(分别)
此方法用于tkinter获取有效的HWND(窗口的标识符),然后从中ctypes获取设备上下文(DC)和硬件详细信息。
# Our convertion from millimeters to inches
MM_TO_IN = 0.0393700787
# Import the libraries
import ctypes
import math
import tkinter
# Set process DPI awareness
ctypes.windll.shcore.SetProcessDpiAwareness(1)
# Create a tkinter window
root = tkinter.Tk()
# Get a DC from the window's HWND
dc = ctypes.windll.user32.GetDC(root.winfo_id())
# The the monitor phyical width
# (returned in millimeters then converted to inches)
mw = ctypes.windll.gdi32.GetDeviceCaps(dc, 4) * MM_TO_IN
# The the monitor phyical height
mh = ctypes.windll.gdi32.GetDeviceCaps(dc, 6) * MM_TO_IN
# Get the monitor horizontal resolution
dw = ctypes.windll.gdi32.GetDeviceCaps(dc, 8)
# Get the monitor vertical resolution
dh = ctypes.windll.gdi32.GetDeviceCaps(dc, 10)
# Destroy the window
root.destroy()
# Horizontal and vertical DPIs calculated
hdpi, vdpi = dw / mw, dh / mh
# Diagonal DPI calculated using Pythagoras
ddpi = math.hypot(dw, dh) / math.hypot(mw, mh)
# Print the DPIs
print(round(hdpi, 1), round(vdpi, 1), round(ddpi, 1))
Run Code Online (Sandbox Code Playgroud)
虽然我说我想避免它,但有一种非常简单的方法可以使用 PyQt5 实现这一点。我想得越多,我就越认为这可能是最好的解决方案,因为它在很大程度上是独立于平台的:
import sys
from PyQt5.QtWidgets import QApplication
app = QApplication(sys.argv)
screen = app.screens()[0]
dpi = screen.physicalDotsPerInch()
app.quit()
Run Code Online (Sandbox Code Playgroud)
请注意,app.screens()返回屏幕列表。在我的情况下,我只连接了一个,但你可能有多个,所以一定要知道你需要从哪个屏幕获取 dpi。如果你把所有这些都包含在一个函数中,它就不会用 PyQt 垃圾来弄乱你的命名空间。
此外,有关 QScreen(sc是 QScreen 对象)的更多信息,请参阅此文档页面:https :
//doc.qt.io/qt-5/qscreen.html
您可以从中提取各种很酷的东西。
| 归档时间: |
|
| 查看次数: |
6955 次 |
| 最近记录: |