After hours of customization, I decided to use this code, which allows me to get around the familiar blurry / fuzzy text issue in Windows 10 on high DPI displays when using the Tkinter interfaces in Python 3.
I did not want to set the compatibility flag or expect others to do this, and I found that by noting the DPI awareness of 'on' through a DLL call and then extracting the DPI parameter, I could enlarge the GUI window and the borders inside.
However, before passing it on to others, I wanted to check if my approach of passing the "GUI" (an instance of tkinter.Tk ()) to the MakeTkDPIAware function in the main part of the body is suitable and gets this function to add custom properties to it is a healthy choice or may cause problems with the tkinter instance. The added properties are then available for use in the main body, but can it be assumed that this will always happen?I was able to find out if this practice is known - and if it frowned or a poor design choice. (So โโoften in Python, I may be so glad to get something working that I forgot to check this question at the time), so I hope someone can advise. It seemed that the most accurate way to โrememberโ the scaling data, rather than create 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):
import os
if os.name == "nt":
from ctypes import windll, pointer, wintypes
try:
windll.shcore.SetProcessDpiAwareness(1)
except Exception:
pass
DPI100pc = 96
DPI_type = 0
winH = wintypes.HWND(window_handle)
monitorhandle = windll.user32.MonitorFromWindow(winH, wintypes.DWORD(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
else:
return None, None, 1
def TkGeometryScale(s, cvtfunc):
patt = r"(?P<W>\d+)x(?P<H>\d+)\+(?P<X>\d+)\+(?P<Y>\d+)"
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)
import tkinter
GUI = tkinter.Tk()
MakeTkDPIAware(GUI)
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))