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))
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..
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:
Find your python.exe
and pythonw.exe
executables
Right click on python.exe
and click "Properties"
Click on the "Compatibility" tab
Click the "Change high DPI settings" button
Tick "Override high DPI scaling behaviour" and select "Application" in the drop-down menu
Repeat for pythonw.exe
if necessary
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With