J'utilise Python pour créer une application de rendu d'image qui rend des tableaux numpy sous forme d'images. J'ai besoin d'un moyen d'automatiser l'écran de calcul DPI, car je génère des images de la même taille sur différents moniteurs (par exemple, 2x2 pouces). Je suis sous Windows 10, Python 3.7.
Je ne demande pas comment obtenir la taille d'écran du moniteur - comme savoir si votre moniteur fait 3000x2000 pixels (comme a été résolu à l'adresse: Comment obtenir la résolution du moniteur en Python ? ).
Je veux plutôt quelque chose qui calcule le DPI spécifiquement, et qui prend en compte tous les paramètres d'échelle appliqués par l'utilisateur et qui pourraient changer le DPI de son système. Par exemple, j'ai une échelle de 250%.
Suite à cette réponse sur experts-exchange ( https://www.experts-exchange.com/questions/21794611/Detecting-system-DPI-settings-in-Python.html ), j'ai essayé d'utiliser ceci: p >
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
Cela semble être la bonne approximation, mais renvoie un nombre trop faible. Il renvoie 240, alors que mon DPI réel est plus proche de 285.
Je préférerais éviter d'utiliser un framework GUI comme Qt pour cela, car je veux minimiser la surcharge, mais si c'est la seule façon, alors je volonté! Si je dois utiliser un framework, je préférerais PyQt5.
3 Réponses :
Bien que j'aie dit que je voulais l'éviter, il existe un moyen très simple de le faire en utilisant PyQt5. Plus j'y pense, plus je pense que cela pourrait être la meilleure solution, car elle est largement indépendante de la plate-forme:
import sys from PyQt5.QtWidgets import QApplication app = QApplication(sys.argv) screen = app.screens()[0] dpi = screen.physicalDotsPerInch() app.quit()
Notez que app.screens ()
renvoie une liste d'écrans. Dans mon cas, je n'en ai qu'un seul joint, mais vous pouvez en avoir plusieurs, alors assurez-vous de savoir sur quel écran vous devez obtenir des dpi. Et si vous gardez tout cela contenu dans une fonction, cela n'encombrera pas votre espace de noms avec des fichiers indésirables PyQt.
Aussi, pour en savoir plus sur QScreen ( sc
est un objet QScreen) voir cette page de documentation:
https://doc.qt.io/qt-5/qscreen.html
Il y a toutes sortes de trucs sympas que vous pouvez en tirer.
Bien que la réponse de @ eric fonctionne, je préférerais éviter de quitter la bibliothèque standard (en ignorant les distributions de Python qui ne 'n'inclut pas ctypes
).
Cela m'a d'abord conduit à ctypes et vous pouvez voir ma réponse initiale (très compliquée) qui peut être trouvée ci-dessous. Plus récemment, j'ai trouvé un moyen de le faire avec juste Tk (je ne me souviens pas du lien vers celui-ci, je vais donc créer un lien vers une réponse Stack Overflow a> qui a le même contenu)
# 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))
Réponse originale
La réponse suivante propose deux solutions:
Le premier est le DPI signalé par Windows en raison de la mise à l'échelle de l'affichage de l'utilisateur
Le second est le DPI réel du moniteur calculé en trouvant la taille physique et la résolution du moniteur
Ces solutions supposent qu'il n'y a qu'un seul moniteur et définissent également la prise en compte du DPI du processus (qui ne convient pas à certains contextes). Pour plus de détails sur les appels d'API Windows effectués par ctypes
, consultez la documentation de SetProcessDPIAwareness
, GetDPIForWindow
, GetDC
et GetDeviceCaps
.
Solution 1
Cette solution ne renvoie pas un DPI correct, et est à la place juste le "facteur de zoom" de Window pour ce moniteur. Pour obtenir le facteur, vous devez diviser le DPI retourné par 96
(ce qui signifie que 96
est un facteur d'échelle de 100%
tandis que 120 signifie un facteur d'échelle de
125%
, etc.).
La méthode utilise tkinter
pour obtenir un HWND code> (un identifiant pour la fenêtre) puis
ctypes
pour obtenir le DPI de la nouvelle fenêtre.
# 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()
Solution 2 strong>
Cette méthode devrait renvoyer le vrai DPI du moniteur, cependant, deux choses doivent être notées:
La taille physique du moniteur est (parfois) signalée de manière incorrecte. Cela entraînera donc une valeur DPI complètement incorrecte, bien que je ne sois pas sûr de savoir comment éviter cela, sauf ajouter une vérification pour s'assurer qu'elle se trouve entre des valeurs raisonnables (quoi que cela signifie!).
La résolution utilisée par Windows est souvent différente de la résolution réelle du moniteur. Cette méthode calcule le vrai DPI du moniteur, mais si vous souhaitez calculer le nombre de pixels virtuels par pouce physique, vous remplacez l'affectation de dw
et dh
par root.winfo_screenwidth ()
et root.winfo_screenheight ()
(respectivement)
Cette méthode utilise tkinter
pour obtenir un HWND
valide (un identifiant pour la fenêtre) puis ctypes
pour obtenir un Contexte de l'appareil (DC) et détails sur le matériel.
import tkinter root = tkinter.Tk() dpi = root.winfo_fpixels('1i')
Je n'ai qu'un seul moniteur et je suis sous Windows10, mais vos deux solutions différentes donnent deux réponses très différentes, qui ne sont ni les 100
que j'attendais. La première solution donne 96
, la seconde solution donne 141,8 141,4 141,7
La première solution ne renvoie pas un DPI correct, mais est simplement le "facteur de zoom" de Windows pour ce moniteur ( 96
signifie que vous l'avez défini sur 100%
- < code> 125% indiquerait 120
DPI). La deuxième réponse ne doit donner une lecture incorrecte que si les dimensions physiques sont incorrectement signalées ou si le 100
que vous avez indiqué est calculé en utilisant la résolution que vous conduisez plutôt que la résolution réelle de votre moniteur (vous pouvez vérifier les nombres à aide au débogage).
Remarque: si vous ne vouliez pas le nombre de pixels virtuels par pouce physique, vous remplaceriez l'affectation de dw
et dh
par root.winfo_screenwidth () code> et
root.winfo_screenheight ()
(respectivement)
C'est pourquoi j'utilise juste Qt. Les choses se compliquent quand je ne le fais pas. Ils sont simples / pythoniques quand je le fais. :)
@ProQ Je ne sais pas s'il y a une relation ici ou si c'est une coïncidence, mais 100 x sqrt (2) == 141.4
.
Si vous ne souhaitez pas utiliser Qt, vous pouvez utiliser votre dernière approche car dans mon cas, il y a une différence de 45,95 dpi entre une solution Qt et une solution ctypes. Je pense que c'est toujours la même chose pour tous les ordinateurs portables. Ajoutez simplement (45,96 dpi) à votre résultat chaque fois que vous utilisez des ctypes.
Très intéressant, je n'ai pas vérifié cela mais si cela fonctionne, ce serait vraiment bien!
Peut-être avez-vous un DPI par ensemble de moniteurs? Dans ce cas, essayez
GetDpiForWindow ()
avecwinDpi = user32.GetDpiForWindow (window)
@mnistic cela semble cool, mais cela ne semble pas fonctionner pour moi: quand je l'essaye pour
window = 0
dans mon code, je viens de revenir 0. À moins que je ne le configure mal?Passez le handle de fenêtre réel à
GetDpiForWindow ()
, utilisezGetDpiForSystem ()
pour obtenir le DPI du système, c'est-à-diresysDpi = user32.GetDpiForSystem ()
@mnistic cela semble être une excellente réponse :) Le GetDpiForSystem renvoie 240, ce que mon essai initial renvoie. Évidemment, je ne suis pas très bon avec tout ce truc
windll.user32
: /