计算屏幕DPI

时间:2019-01-19 22:21:51

标签: python dpi

我正在使用Python构建一个图像渲染应用程序,该程序将numpy数组呈现为图像。我需要一种自动计算屏幕DPI的方法,因为我正在不同的显示器(例如2x2英寸)上生成相同大小的图像。我在Windows 10,Python 3.7中。

我不是不是在询问如何获取显示器的屏幕尺寸-就像找出显示器是否为3000x2000像素(已在How do I get monitor resolution in Python?解决) )。

相反,我想要一种专门计算DPI的东西,并且考虑到用户应用的可能更改其系统DPI的任何比例设置。例如,我将比例设置为250%。

在Experts-exchange(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

这似乎是正确的选择,但返回的数字太低。它返回240,而我的实际DPI接近285。

我希望避免使用Qt这样的GUI框架,因为我想最大程度地减少开销,但是如果那是唯一的方法,那么我会的!如果必须使用框架,则最好使用PyQt5。

3 个答案:

答案 0 :(得分:2)

虽然我说过我想避免这种情况,但是有一种非常简单的方法可以使用PyQt5做到这一点。我考虑得越多,就越可能是最好的解决方案,因为它在很大程度上与平台无关:

import sys
from PyQt5.QtWidgets import QApplication
app = QApplication(sys.argv)
screen = app.screens()[0]
dpi = screen.physicalDotsPerInch()
app.quit()

请注意,app.screens()返回屏幕列表。就我而言,我只连接了一个屏幕,但您可能有多个屏幕,因此请务必注意需要从哪个屏幕获得dpi。而且,如果将所有这些内容都包含在一个函数中,它不会因PyQt垃圾而弄乱您的名称空间。

此外,有关QScreen(sc是QScreen对象)的更多信息,请参见以下文档页面:
https://doc.qt.io/qt-5/qscreen.html

您可以从中汲取各种有趣的东西。

答案 1 :(得分:1)

虽然@eric's的答案有效,但我还是希望避免离开标准库(忽略不包含ctypes的Python发行版)。

这最初使我进入了ctypes,您可以看到我的初始(非常复杂)的答案,可以在下面找到。最近,我找到了一种仅用Tk的方法(我不记得它的链接,所以将链接到具有相同内容的Stack Overflow answer

import tkinter
root = tkinter.Tk()
dpi = root.winfo_fpixels('1i')

原始答案

以下答案提供了两种解决方案:

  1. 首先是Windows报告的DPI,这是由于用户的显示缩放所致

  2. 第二个是通过找到显示器的物理尺寸和分辨率计算出的显示器的真实DPI

这些解决方案假定只有一台监视器,并且还设置了进程DPI意识(这不适用于某些情况)。有关ctypes进行的Windows API调用的详细信息,请参见SetProcessDPIAwarenessGetDPIForWindowGetDCGetDeviceCaps的文档。

解决方案1 ​​

此解决方案未返回正确的DPI,而只是该监视器的Window的“缩放因子”。要获得该因子,您应该将返回的DPI除以96(这意味着96100%的比例因子,而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()

解决方案2

此方法应返回监视器的真实DPI,但是,应注意两点:

  1. (有时)错误报告了显示器的物理尺寸。因此,这将导致完全不正确的DPI值,尽管我不确定如何防止这种情况,只能添加检查以确保它在合理的值之间(无论如何!)。

  2. Windows使用的分辨率通常与显示器的实际分辨率不同。此方法计算监视器的真实DPI,但是如果要计算每物理英寸的虚拟像素数,则可以将dwdh的分配替换为root.winfo_screenwidth()和{{1 }}(分别)

此方法使用root.winfo_screenheight()获取有效的tkinter(窗口的标识符),然后使用HWND从中获取设备上下文(DC)和硬件详细信息。

ctypes

答案 2 :(得分:0)

如果您不想使用Qt,则可以使用最后一种方法,因为在我的情况下,Qt解决方案和ctypes解决方案之间存在45.95 dpi的差异。我认为所有笔记本电脑都一样。每次使用ctypes时,只需在结果上加上(45.96 dpi)。

screen shot for representation