Merge branch 'test_scaling'

# Conflicts:
#	customtkinter/widgets/ctk_button.py
#	customtkinter/widgets/ctk_checkbox.py
#	customtkinter/widgets/ctk_entry.py
#	customtkinter/widgets/ctk_label.py
#	customtkinter/widgets/ctk_progressbar.py
#	customtkinter/widgets/ctk_radiobutton.py
#	customtkinter/widgets/ctk_slider.py
#	customtkinter/widgets/ctk_switch.py
#	customtkinter/widgets/customtkinter_frame.py
This commit is contained in:
Tom Schimansky 2022-04-21 09:42:24 +02:00
commit 59a8574f4c
8 changed files with 106 additions and 7 deletions

View File

@ -16,7 +16,7 @@ from .widgets.ctk_toplevel import CTkToplevel
from .customtkinter_settings import CTkSettings
from .appearance_mode_tracker import AppearanceModeTracker
from .customtkinter_theme_manager import CTkThemeManager
from .theme_manager import CTkThemeManager
from distutils.version import StrictVersion as Version
import tkinter

View File

@ -3,11 +3,9 @@ import sys
class CTkSettings:
scaling_factor = 1
circle_font_is_ready = False
hand_cursor_enabled = True
preferred_drawing_method = None
radius_to_char_fine = None
@classmethod
@ -42,7 +40,6 @@ class CTkSettings:
@classmethod
def print_settings(cls):
print(f"CTkSettings current values:")
print(f"scaling_factor = {cls.scaling_factor}")
print(f"circle_font_is_ready = {cls.circle_font_is_ready}")
print(f"hand_cursor_enabled = {cls.hand_cursor_enabled}")
print(f"preferred_drawing_method = {cls.preferred_drawing_method}")

View File

@ -0,0 +1,100 @@
import tkinter
import sys
class ScalingTracker:
window_widgets_dict = {} # contains window objects as keys with list of widget callbacks as elements
window_dpi_scaling_dict = {} # contains window objects as keys and corresponding DPI values
user_scaling = 1 # scale change of all widgets and windows
update_loop_running = False
@classmethod
def get_widget_scaling(cls, widget):
window_root = cls.get_window_root_of_widget(widget)
return cls.window_dpi_scaling_dict[window_root] * cls.user_scaling
@classmethod
def get_window_scaling(cls, window):
return cls.window_dpi_scaling_dict[window] * cls.user_scaling
@classmethod
def set_user_scaling(cls, user_scaling_factor):
cls.user_scaling = user_scaling_factor
cls.update_scaling_callbacks()
@classmethod
def get_window_root_of_widget(cls, widget):
current_widget = widget
while isinstance(current_widget, tkinter.Tk) is False and\
isinstance(current_widget, tkinter.Toplevel) is False:
current_widget = current_widget.master
return current_widget
@classmethod
def update_scaling_callbacks(cls):
for window, callback_list in cls.window_widgets_dict.items():
for callback in callback_list:
callback(cls.window_dpi_scaling_dict[window])
@classmethod
def add_widget(cls, widget_callback, widget):
window_root = cls.get_window_root_of_widget(widget)
if window_root not in cls.window_widgets_dict:
cls.window_widgets_dict[window_root] = [widget_callback]
else:
cls.window_widgets_dict[window_root].append(widget_callback)
if window_root not in cls.window_dpi_scaling_dict:
cls.window_dpi_scaling_dict[window_root] = cls.get_window_dpi_value(window_root)
if not cls.update_loop_running:
window_root.after(100, cls.check_dpi_scaling)
cls.update_loop_running = True
@classmethod
def add_window(cls, window_callback, window):
if window not in cls.window_widgets_dict:
cls.window_widgets_dict[window] = [window_callback]
else:
cls.window_widgets_dict[window].append(window_callback)
if window not in cls.window_dpi_scaling_dict:
cls.window_dpi_scaling_dict[window] = cls.get_window_dpi_value(window)
@classmethod
def get_window_dpi_value(cls, window):
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)
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
@classmethod
def check_dpi_scaling(cls):
# find an existing tkinter object for the next call of .after()
for root_tk in cls.window_widgets_dict.keys():
try:
root_tk.after(500, cls.check_dpi_scaling)
return
except Exception:
continue
cls.update_loop_running = False

View File

@ -7,7 +7,7 @@ from .ctk_frame import CTkFrame
from .ctk_toplevel import CTkToplevel
from .ctk_button import CTkButton
from ..appearance_mode_tracker import AppearanceModeTracker
from ..customtkinter_theme_manager import CTkThemeManager
from ..theme_manager import CTkThemeManager
class CTkInputDialog:

View File

@ -6,7 +6,7 @@ import platform
import ctypes
from ..appearance_mode_tracker import AppearanceModeTracker
from ..customtkinter_theme_manager import CTkThemeManager
from ..theme_manager import CTkThemeManager
class CTk(tkinter.Tk):

View File

@ -6,7 +6,7 @@ import platform
import ctypes
from ..appearance_mode_tracker import AppearanceModeTracker
from ..customtkinter_theme_manager import CTkThemeManager
from ..theme_manager import CTkThemeManager
class CTkToplevel(tkinter.Toplevel):

View File

@ -15,6 +15,8 @@ class ExampleApp(customtkinter.CTk):
window = customtkinter.CTkToplevel(self)
window.geometry("400x200")
print(window.master.winfo_class())
label = customtkinter.CTkLabel(window, text="CTkToplevel window")
label.pack(side="top", fill="both", expand=True, padx=40, pady=40)