Open TomSchimansky opened 2 years ago
Try adding this:
import ctypes
ctypes.windll.shcore.SetProcessDpiAwareness(0)
I already set the dpi awareness in the scaling tracker class, you can have a look if you are interested. These are the methods that manage the scaling:
@classmethod
def activate_high_dpi_awareness(cls):
""" make process DPI aware, customtkinter elements will get scaled automatically,
only gets activated when CTk object is created """
if not cls.deactivate_automatic_dpi_awareness:
if sys.platform == "darwin":
pass # high DPI scaling works automatically on macOS
elif sys.platform.startswith("win"):
from ctypes import windll
windll.shcore.SetProcessDpiAwareness(2)
# Microsoft Docs: https://docs.microsoft.com/en-us/windows/win32/api/shellscalingapi/ne-shellscalingapi-process_dpi_awareness
else:
pass # DPI awareness on Linux not implemented
@classmethod
def get_window_dpi_scaling(cls, window) -> float:
if not cls.deactivate_automatic_dpi_awareness:
if sys.platform == "darwin":
return 1 # scaling works automatically on macOS
elif sys.platform.startswith("win"):
from ctypes import windll, pointer, wintypes
DPI100pc = 96 # DPI 96 is 100% scaling
DPI_type = 0 # MDT_EFFECTIVE_DPI = 0, MDT_ANGULAR_DPI = 1, MDT_RAW_DPI = 2
window_hwnd = wintypes.HWND(window.winfo_id())
monitor_handle = windll.user32.MonitorFromWindow(window_hwnd, wintypes.DWORD(2)) # MONITOR_DEFAULTTONEAREST = 2
x_dpi, y_dpi = wintypes.UINT(), wintypes.UINT()
windll.shcore.GetDpiForMonitor(monitor_handle, DPI_type, pointer(x_dpi), pointer(y_dpi))
return (x_dpi.value + y_dpi.value) / (2 * DPI100pc)
else:
return 1 # DPI awareness on Linux not implemented
else:
return 1
@classmethod
def check_dpi_scaling(cls):
new_scaling_detected = False
# check for every window if scaling value changed
for window in cls.window_widgets_dict:
if window.winfo_exists() and not window.state() == "iconic":
current_dpi_scaling_value = cls.get_window_dpi_scaling(window)
if current_dpi_scaling_value != cls.window_dpi_scaling_dict[window]:
cls.window_dpi_scaling_dict[window] = current_dpi_scaling_value
if sys.platform.startswith("win"):
window.attributes("-alpha", 0.15)
cls.update_scaling_callbacks_for_window(window)
if sys.platform.startswith("win"):
window.after(200, lambda: window.attributes("-alpha", 1))
new_scaling_detected = True
# find an existing tkinter object for the next call of .after()
for app in cls.window_widgets_dict.keys():
try:
if new_scaling_detected:
app.after(cls.loop_pause_after_new_scaling, cls.check_dpi_scaling)
else:
app.after(cls.update_loop_interval, cls.check_dpi_scaling)
return
except Exception:
continue
cls.update_loop_running = False
The set scaling method of the CTk window class looks like the following:
def _set_scaling(self, new_widget_scaling, new_spacing_scaling, new_window_scaling):
self._window_scaling = new_window_scaling
# block update_dimensions_event to prevent current_width and current_height to get updated
self._block_update_dimensions_event = True
# force new dimensions on window by using min, max, and geometry
super().minsize(self._apply_window_scaling(self._current_width), self._apply_window_scaling(self._current_height))
super().maxsize(self._apply_window_scaling(self._current_width), self._apply_window_scaling(self._current_height))
super().geometry(f"{self._apply_window_scaling(self._current_width)}x{self._apply_window_scaling(self._current_height)}")
# set new scaled min and max with 400ms delay (otherwise it won't work for some reason)
self.after(400, self._set_scaled_min_max)
# release the blocking of update_dimensions_event after a small amount of time (slight delay is necessary)
def set_block_update_dimensions_event_false():
self._block_update_dimensions_event = False
self.after(100, lambda: set_block_update_dimensions_event_false())
Its very difficult to spot any errors in this process I implemented, but maybe this is also just a tk bug, I don't know...
I will work on this later if I have the time
Window changes size when dragged from over to monitor with different scaling one Windows 11, maybe also Windows 10. This only happens when window is hold by the mouse for a second after moving to the other monitor, and is then released by the mouse.