3

I have written an application in python 3.6 and would like to run a command to see what the current scaling for a monitor is in Windows 10 or 8 - something like how the following returns screen resolution:

user32 = ctypes.windll.user32
screensize_l = user32.GetSystemMetrics(0)
screensize_w = user32.GetSystemMetrics(1)

I understand that the easiest way to do this may be to make my application DPI aware, but doing so causes lots of additional problems in my application - so I would like to avoid this option.

I have looked in the windows documentation and thought "GetDpiForMonitor" or "GetScaleFactorForMonitor" may be what I am looking for, but don't know how to implement these commands.

I already use both win32api and ctypes so anything relying on either of these would be fine - any help would be greatly appreciated!

1
  • I solved my own problem by painstakingly making my program re scale all fixed elements based on the size of the screen returned by user32.GetSystemMetrics(0) and user32.GetSystemMetrics(1). I just calculated an adjustment factor associated with my original atticipated screensize. Commented Jun 22, 2018 at 14:22

2 Answers 2

6

On Windows 10, the following code (you need pywin32):

import ctypes
import win32api

PROCESS_PER_MONITOR_DPI_AWARE = 2
MDT_EFFECTIVE_DPI = 0

def print_dpi():
    shcore = ctypes.windll.shcore
    monitors = win32api.EnumDisplayMonitors()
    hresult = shcore.SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE)
    assert hresult == 0
    dpiX = ctypes.c_uint()
    dpiY = ctypes.c_uint()
    for i, monitor in enumerate(monitors):
        shcore.GetDpiForMonitor(
            monitor[0].handle,
            MDT_EFFECTIVE_DPI,
            ctypes.byref(dpiX),
            ctypes.byref(dpiY)
        )
        print(
            f"Monitor {i} (hmonitor: {monitor[0]}) = dpiX: {dpiX.value}, dpiY: {dpiY.value}"
        )


if __name__ == "__main__":
    print_dpi()

gives the following output on my machine with 3 monitors and 1 scaled to 125%:

Monitor 0 (hmonitor: <PyHANDLE:65539>) = dpiX: 96, dpiY: 96
Monitor 1 (hmonitor: <PyHANDLE:65537>) = dpiX: 120, dpiY: 120
Monitor 2 (hmonitor: <PyHANDLE:65541>) = dpiX: 96, dpiY: 96

If you do not want to set your own application as a dpi-aware per monitor, you could try launching some variant of the above code in another process using the multiprocessing module and retrieve the results.

hope this helps.

Sign up to request clarification or add additional context in comments.

Comments

2

You can get the scaling factor using ctype and then modify your dimensions according to it.

import ctypes

scale_factor = ctypes.windll.shcore.GetScaleFactorForDevice(0)

If scale factor = 150%, this will return 150.
Its probably useful to divide by 100 when your working with this number in your app.

2 Comments

From the documentation for GetScaleFactorForDevice: "This function is not supported as of Windows 8.1. Use GetScaleFactorForMonitor instead."
Thanks for that info. I just tested it and its still working for me (Windows 10). I use it in my code on multiple windows machines without issue. I think it can help other people out as well.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.