Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Attempting to resolve blurred tkinter text + scaling on Windows 10 high DPI displays, but concerned my approach is not Pythonic or is unsafe

After hours of tweaking I have settled on this code which allows me to get round the familiar problem of blurry / fuzzy text in Windows 10 on high DPI displays when using Tkinter interfaces in Python 3.

I didn't want to have to set the compatibility flag or expect others to do it and I discovered that by flagging DPI awareness 'on' through a DLL call and then retrieving the DPI setting I could then scale up the GUI window and frames within.

Before passing this to others, however, I wanted to check if my approach of passing 'GUI' (a tkinter.Tk() instance) to the MakeTkDPIAware function in the main body and getting that function to add custom properties to it is a healthy choice or risks causing problems to the tkinter instance. The properties added are then available for use in the main body, but is it safe to assume that will always happen?

I have been able to find out if this practice is a known one - and if it is frowned upon or a poor design choice. (So often in Python, I can be so excited to get something working that I forget to check this type of question at the time), so I hope someone can advise. It seemed the tidiest way to 'remember' the scaling data, rather than creating a new global variable.

I would be very interested to hear if another solution would be more Pythonic.

import re


def Get_HWND_DPI(window_handle):
    #To detect high DPI displays and avoid need to set Windows compatibility flags
    import os
    if os.name == "nt":
        from ctypes import windll, pointer, wintypes
        try:
            windll.shcore.SetProcessDpiAwareness(1)
        except Exception:
            pass  # this will fail on Windows Server and maybe early Windows
        DPI100pc = 96  # DPI 96 is 100% scaling
        DPI_type = 0  # MDT_EFFECTIVE_DPI = 0, MDT_ANGULAR_DPI = 1, MDT_RAW_DPI = 2
        winH = wintypes.HWND(window_handle)
        monitorhandle = windll.user32.MonitorFromWindow(winH, wintypes.DWORD(2))  # MONITOR_DEFAULTTONEAREST = 2
        X = wintypes.UINT()
        Y = wintypes.UINT()
        try:
            windll.shcore.GetDpiForMonitor(monitorhandle, DPI_type, pointer(X), pointer(Y))
            return X.value, Y.value, (X.value + Y.value) / (2 * DPI100pc)
        except Exception:
            return 96, 96, 1  # Assume standard Windows DPI & scaling
    else:
        return None, None, 1  # What to do for other OSs?


def TkGeometryScale(s, cvtfunc):
    patt = r"(?P<W>\d+)x(?P<H>\d+)\+(?P<X>\d+)\+(?P<Y>\d+)"  # format "WxH+X+Y"
    R = re.compile(patt).search(s)
    G = str(cvtfunc(R.group("W"))) + "x"
    G += str(cvtfunc(R.group("H"))) + "+"
    G += str(cvtfunc(R.group("X"))) + "+"
    G += str(cvtfunc(R.group("Y")))
    return G


def MakeTkDPIAware(TKGUI):
    TKGUI.DPI_X, TKGUI.DPI_Y, TKGUI.DPI_scaling = Get_HWND_DPI(TKGUI.winfo_id())
    TKGUI.TkScale = lambda v: int(float(v) * TKGUI.DPI_scaling)
    TKGUI.TkGeometryScale = lambda s: TkGeometryScale(s, TKGUI.TkScale)


#Example use:
import tkinter


GUI = tkinter.Tk()
MakeTkDPIAware(GUI)  # Sets the windows flag + gets adds .DPI_scaling property
GUI.geometry(GUI.TkGeometryScale("600x200+200+100"))
gray = "#cccccc"
DemoFrame = tkinter.Frame(GUI, width=GUI.TkScale(580), height=GUI.TkScale(180), background=gray)
DemoFrame.place(x=GUI.TkScale(10), y=GUI.TkScale(10))
DemoFrame.pack_propagate(False)
LabelText = "Scale = " + str(GUI.DPI_scaling)
DemoLabel = tkinter.Label(DemoFrame, text=LabelText, width=10, height=1)
DemoLabel.pack(pady=GUI.TkScale(70))
like image 277
dingles Avatar asked Dec 24 '16 18:12

dingles


2 Answers

Simply using

from ctypes import windll
windll.shcore.SetProcessDpiAwareness(1)

and allowing font size adjustment solved my problems on Windows 10.

Not sure why no one commented or assisted though. The blurry fonts are a pain, I would expect this thread had a debate and a best solution for this Tkinter nuisance..

like image 70
Jay Avatar answered Dec 19 '22 00:12

Jay


Not exactly answering your question but if you want to fix blurriness of tkinter windows on high dpi displays in Windows 10 (Fall Creators update) without having to insert lines of python code:

  1. Find your python.exe and pythonw.exe executables

  2. Right click on python.exe and click "Properties"

  3. Click on the "Compatibility" tab

  4. Click the "Change high DPI settings" button

  5. Tick "Override high DPI scaling behaviour" and select "Application" in the drop-down menu

  6. Repeat for pythonw.exe if necessary

like image 30
binaryfunt Avatar answered Dec 18 '22 23:12

binaryfunt