added no_color_updates option for set_scaling methods, added transparency effect on Windows when window scaling, changed scaling loop times

This commit is contained in:
TomSchimansky 2022-10-15 01:02:54 +02:00
parent 205cdae5f9
commit 9ffe61dd54
18 changed files with 70 additions and 57 deletions

View File

@ -1,6 +1,6 @@
import tkinter import tkinter
import sys import sys
from typing import Callable from typing import Callable, Dict
class ScalingTracker: class ScalingTracker:
@ -14,7 +14,8 @@ class ScalingTracker:
spacing_scaling = 1 spacing_scaling = 1
update_loop_running = False update_loop_running = False
update_loop_interval = 150 # milliseconds update_loop_interval = 600 # ms
loop_pause_after_new_scaling = 1000 # ms
@classmethod @classmethod
def get_widget_scaling(cls, widget) -> float: def get_widget_scaling(cls, widget) -> float:
@ -162,18 +163,32 @@ class ScalingTracker:
@classmethod @classmethod
def check_dpi_scaling(cls): def check_dpi_scaling(cls):
new_scaling_detected = False
# check for every window if scaling value changed # check for every window if scaling value changed
for window in cls.window_widgets_dict: for window in cls.window_widgets_dict:
if window.winfo_exists(): if window.winfo_exists() and not window.state() == "iconic":
current_dpi_scaling_value = cls.get_window_dpi_scaling(window) current_dpi_scaling_value = cls.get_window_dpi_scaling(window)
if current_dpi_scaling_value != cls.window_dpi_scaling_dict[window]: if current_dpi_scaling_value != cls.window_dpi_scaling_dict[window]:
cls.window_dpi_scaling_dict[window] = current_dpi_scaling_value 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) 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() # find an existing tkinter object for the next call of .after()
for app in cls.window_widgets_dict.keys(): for app in cls.window_widgets_dict.keys():
try: try:
app.after(cls.update_loop_interval, cls.check_dpi_scaling) 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 return
except Exception: except Exception:
continue continue

View File

@ -106,15 +106,11 @@ class CTkButton(CTkBaseClass):
super()._set_scaling(*args, **kwargs) super()._set_scaling(*args, **kwargs)
if self._text_label is not None: if self._text_label is not None:
self._text_label.destroy() self._text_label.configure(font=self._apply_font_scaling(self._font))
self._text_label = None
if self._image_label is not None:
self._image_label.destroy()
self._image_label = None
self._canvas.configure(width=self._apply_widget_scaling(self._desired_width), self._canvas.configure(width=self._apply_widget_scaling(self._desired_width),
height=self._apply_widget_scaling(self._desired_height)) height=self._apply_widget_scaling(self._desired_height))
self._draw() self._draw(no_color_updates=True)
def _set_dimensions(self, width: int = None, height: int = None): def _set_dimensions(self, width: int = None, height: int = None):
super()._set_dimensions(width, height) super()._set_dimensions(width, height)

View File

@ -137,7 +137,7 @@ class CTkCheckBox(CTkBaseClass):
height=self._apply_widget_scaling(self._desired_height)) height=self._apply_widget_scaling(self._desired_height))
self._canvas.configure(width=self._apply_widget_scaling(self._checkbox_width), self._canvas.configure(width=self._apply_widget_scaling(self._checkbox_width),
height=self._apply_widget_scaling(self._checkbox_height)) height=self._apply_widget_scaling(self._checkbox_height))
self._draw() self._draw(no_color_updates=True)
def _set_dimensions(self, width: int = None, height: int = None): def _set_dimensions(self, width: int = None, height: int = None):
super()._set_dimensions(width, height) super()._set_dimensions(width, height)

View File

@ -135,7 +135,7 @@ class CTkComboBox(CTkBaseClass):
self._canvas.configure(width=self._apply_widget_scaling(self._desired_width), self._canvas.configure(width=self._apply_widget_scaling(self._desired_width),
height=self._apply_widget_scaling(self._desired_height)) height=self._apply_widget_scaling(self._desired_height))
self._draw() self._draw(no_color_updates=True)
def _set_dimensions(self, width: int = None, height: int = None): def _set_dimensions(self, width: int = None, height: int = None):
super()._set_dimensions(width, height) super()._set_dimensions(width, height)

View File

@ -115,7 +115,7 @@ class CTkEntry(CTkBaseClass):
padx=self._apply_widget_scaling(self._corner_radius) if self._corner_radius >= 6 else self._apply_widget_scaling(6)) padx=self._apply_widget_scaling(self._corner_radius) if self._corner_radius >= 6 else self._apply_widget_scaling(6))
self._canvas.configure(width=self._apply_widget_scaling(self._desired_width), height=self._apply_widget_scaling(self._desired_height)) self._canvas.configure(width=self._apply_widget_scaling(self._desired_width), height=self._apply_widget_scaling(self._desired_height))
self._draw() self._draw(no_color_updates=True)
def _set_dimensions(self, width=None, height=None): def _set_dimensions(self, width=None, height=None):
super()._set_dimensions(width, height) super()._set_dimensions(width, height)

View File

@ -62,7 +62,7 @@ class CTkFrame(CTkBaseClass):
self._draw_engine = DrawEngine(self._canvas) self._draw_engine = DrawEngine(self._canvas)
self._overwrite_preferred_drawing_method = overwrite_preferred_drawing_method self._overwrite_preferred_drawing_method = overwrite_preferred_drawing_method
self._draw() self._draw(no_color_updates=True)
def winfo_children(self) -> List[any]: def winfo_children(self) -> List[any]:
""" """

View File

@ -88,7 +88,7 @@ class CTkLabel(CTkBaseClass):
self._text_label.grid(row=0, column=0, sticky=text_label_grid_sticky, self._text_label.grid(row=0, column=0, sticky=text_label_grid_sticky,
padx=self._apply_widget_scaling(min(self._corner_radius, round(self._current_height/2)))) padx=self._apply_widget_scaling(min(self._corner_radius, round(self._current_height/2))))
self._draw() self._draw(no_color_updates=True)
def _set_dimensions(self, width=None, height=None): def _set_dimensions(self, width=None, height=None):
super()._set_dimensions(width, height) super()._set_dimensions(width, height)

View File

@ -148,7 +148,7 @@ class CTkOptionMenu(CTkBaseClass):
self._canvas.configure(width=self._apply_widget_scaling(self._desired_width), self._canvas.configure(width=self._apply_widget_scaling(self._desired_width),
height=self._apply_widget_scaling(self._desired_height)) height=self._apply_widget_scaling(self._desired_height))
self._draw() self._draw(no_color_updates=True)
def _set_dimensions(self, width: int = None, height: int = None): def _set_dimensions(self, width: int = None, height: int = None):
super()._set_dimensions(width, height) super()._set_dimensions(width, height)

View File

@ -94,7 +94,7 @@ class CTkProgressBar(CTkBaseClass):
self._canvas.configure(width=self._apply_widget_scaling(self._desired_width), self._canvas.configure(width=self._apply_widget_scaling(self._desired_width),
height=self._apply_widget_scaling(self._desired_height)) height=self._apply_widget_scaling(self._desired_height))
self._draw() self._draw(no_color_updates=True)
def _set_dimensions(self, width=None, height=None): def _set_dimensions(self, width=None, height=None):
super()._set_dimensions(width, height) super()._set_dimensions(width, height)

View File

@ -132,7 +132,7 @@ class CTkRadioButton(CTkBaseClass):
height=self._apply_widget_scaling(self._desired_height)) height=self._apply_widget_scaling(self._desired_height))
self._canvas.configure(width=self._apply_widget_scaling(self._radiobutton_width), self._canvas.configure(width=self._apply_widget_scaling(self._radiobutton_width),
height=self._apply_widget_scaling(self._radiobutton_height)) height=self._apply_widget_scaling(self._radiobutton_height))
self._draw() self._draw(no_color_updates=True)
def _set_dimensions(self, width: int = None, height: int = None): def _set_dimensions(self, width: int = None, height: int = None):
super()._set_dimensions(width, height) super()._set_dimensions(width, height)

View File

@ -114,7 +114,7 @@ class CTkSlider(CTkBaseClass):
self._canvas.configure(width=self._apply_widget_scaling(self._desired_width), self._canvas.configure(width=self._apply_widget_scaling(self._desired_width),
height=self._apply_widget_scaling(self._desired_height)) height=self._apply_widget_scaling(self._desired_height))
self._draw() self._draw(no_color_updates=True)
def _set_dimensions(self, width=None, height=None): def _set_dimensions(self, width=None, height=None):
super()._set_dimensions(width, height) super()._set_dimensions(width, height)

View File

@ -137,7 +137,7 @@ class CTkSwitch(CTkBaseClass):
height=self._apply_widget_scaling(self._desired_height)) height=self._apply_widget_scaling(self._desired_height))
self._canvas.configure(width=self._apply_widget_scaling(self._switch_width), self._canvas.configure(width=self._apply_widget_scaling(self._switch_width),
height=self._apply_widget_scaling(self._switch_height)) height=self._apply_widget_scaling(self._switch_height))
self._draw() self._draw(no_color_updates=True)
def _set_dimensions(self, width: int = None, height: int = None): def _set_dimensions(self, width: int = None, height: int = None):
super()._set_dimensions(width, height) super()._set_dimensions(width, height)

View File

@ -123,7 +123,7 @@ class CTkTabview(CTkBaseClass):
self._canvas.configure(width=self._apply_widget_scaling(self._desired_width), self._canvas.configure(width=self._apply_widget_scaling(self._desired_width),
height=self._apply_widget_scaling(self._desired_height - self._top_spacing - self._top_button_overhang)) height=self._apply_widget_scaling(self._desired_height - self._top_spacing - self._top_button_overhang))
self._draw() self._draw(no_color_updates=True)
def _set_dimensions(self, width=None, height=None): def _set_dimensions(self, width=None, height=None):
super()._set_dimensions(width, height) super()._set_dimensions(width, height)

View File

@ -182,7 +182,7 @@ class CTkTextbox(CTkBaseClass):
self._canvas.configure(width=self._apply_widget_scaling(self._desired_width), self._canvas.configure(width=self._apply_widget_scaling(self._desired_width),
height=self._apply_widget_scaling(self._desired_height)) height=self._apply_widget_scaling(self._desired_height))
self._create_grid_for_text_and_scrollbars(re_grid_textbox=False, re_grid_x_scrollbar=True, re_grid_y_scrollbar=True) self._create_grid_for_text_and_scrollbars(re_grid_textbox=False, re_grid_x_scrollbar=True, re_grid_y_scrollbar=True)
self._draw() self._draw(no_color_updates=True)
def _set_dimensions(self, width=None, height=None): def _set_dimensions(self, width=None, height=None):
super()._set_dimensions(width, height) super()._set_dimensions(width, height)

View File

@ -48,32 +48,31 @@ class DropdownMenu(tkinter.Menu):
""" apply platform specific appearance attributes, configure all colors """ """ apply platform specific appearance attributes, configure all colors """
if sys.platform == "darwin": if sys.platform == "darwin":
self.configure(tearoff=False, super().configure(tearoff=False,
font=self._apply_font_scaling(self._font)) font=self._apply_font_scaling(self._font))
elif sys.platform.startswith("win"): elif sys.platform.startswith("win"):
print("dropdon win") super().configure(tearoff=False,
self.configure(tearoff=False, relief="flat",
relief="flat", activebackground=ThemeManager.single_color(self._hover_color, self._appearance_mode),
activebackground=ThemeManager.single_color(self._hover_color, self._appearance_mode), borderwidth=self._apply_widget_scaling(4),
borderwidth=0, activeborderwidth=self._apply_widget_scaling(4),
activeborderwidth=self._apply_widget_scaling(4), bg=ThemeManager.single_color(self._fg_color, self._appearance_mode),
bg=ThemeManager.single_color(self._fg_color, self._appearance_mode), fg=ThemeManager.single_color(self._text_color, self._appearance_mode),
fg=ThemeManager.single_color(self._text_color, self._appearance_mode), activeforeground=ThemeManager.single_color(self._text_color, self._appearance_mode),
activeforeground=ThemeManager.single_color(self._text_color, self._appearance_mode), font=self._apply_font_scaling(self._font),
font=self._apply_font_scaling(self._font), cursor="hand2")
cursor="hand2")
else: else:
self.configure(tearoff=False, super().configure(tearoff=False,
relief="flat", relief="flat",
activebackground=ThemeManager.single_color(self._hover_color, self._appearance_mode), activebackground=ThemeManager.single_color(self._hover_color, self._appearance_mode),
borderwidth=0, borderwidth=0,
activeborderwidth=0, activeborderwidth=0,
bg=ThemeManager.single_color(self._fg_color, self._appearance_mode), bg=ThemeManager.single_color(self._fg_color, self._appearance_mode),
fg=ThemeManager.single_color(self._text_color, self._appearance_mode), fg=ThemeManager.single_color(self._text_color, self._appearance_mode),
activeforeground=ThemeManager.single_color(self._text_color, self._appearance_mode), activeforeground=ThemeManager.single_color(self._text_color, self._appearance_mode),
font=self._apply_font_scaling(self._font)) font=self._apply_font_scaling(self._font))
def _add_menu_commands(self): def _add_menu_commands(self):
""" delete existing menu labels and createe new labels with command according to values list """ """ delete existing menu labels and createe new labels with command according to values list """
@ -110,15 +109,15 @@ class DropdownMenu(tkinter.Menu):
def configure(self, **kwargs): def configure(self, **kwargs):
if "fg_color" in kwargs: if "fg_color" in kwargs:
self._fg_color = kwargs.pop("fg_color") self._fg_color = kwargs.pop("fg_color")
self.configure(bg=ThemeManager.single_color(self._fg_color, self._appearance_mode)) super().configure(bg=ThemeManager.single_color(self._fg_color, self._appearance_mode))
if "hover_color" in kwargs: if "hover_color" in kwargs:
self._hover_color = kwargs.pop("hover_color") self._hover_color = kwargs.pop("hover_color")
self.configure(activebackground=ThemeManager.single_color(self._hover_color, self._appearance_mode)) super().configure(activebackground=ThemeManager.single_color(self._hover_color, self._appearance_mode))
if "text_color" in kwargs: if "text_color" in kwargs:
self._text_color = kwargs.pop("text_color") self._text_color = kwargs.pop("text_color")
self.configure(fg=ThemeManager.single_color(self._text_color, self._appearance_mode)) super().configure(fg=ThemeManager.single_color(self._text_color, self._appearance_mode))
if "font" in kwargs: if "font" in kwargs:
self._font = kwargs.pop("font") self._font = kwargs.pop("font")
@ -151,6 +150,9 @@ class DropdownMenu(tkinter.Menu):
elif attribute_name == "values": elif attribute_name == "values":
return self._values return self._values
else:
return super().cget(attribute_name)
def _apply_widget_scaling(self, value: Union[int, float, str]) -> Union[float, str]: def _apply_widget_scaling(self, value: Union[int, float, str]) -> Union[float, str]:
if isinstance(value, (int, float)): if isinstance(value, (int, float)):
return value * self._widget_scaling return value * self._widget_scaling
@ -183,10 +185,7 @@ class DropdownMenu(tkinter.Menu):
self._widget_scaling = new_widget_scaling self._widget_scaling = new_widget_scaling
self._spacing_scaling = new_spacing_scaling self._spacing_scaling = new_spacing_scaling
super().configure(font=self._apply_font_scaling(self._font)) self._configure_menu_for_platforms()
if sys.platform.startswith("win"):
self.configure(activeborderwidth=self._apply_widget_scaling(4))
def _set_appearance_mode(self, mode_string): def _set_appearance_mode(self, mode_string):
""" colors won't update on appearance mode change when dropdown is open, because it's not necessary """ """ colors won't update on appearance mode change when dropdown is open, because it's not necessary """

View File

@ -5,6 +5,7 @@ import os
import platform import platform
import ctypes import ctypes
import re import re
import time
from typing import Union, Tuple from typing import Union, Tuple
from ..appearance_mode_tracker import AppearanceModeTracker from ..appearance_mode_tracker import AppearanceModeTracker
@ -78,6 +79,7 @@ class CTk(tkinter.Tk):
def _update_dimensions_event(self, event=None): def _update_dimensions_event(self, event=None):
if not self._block_update_dimensions_event: if not self._block_update_dimensions_event:
self.update_idletasks()
detected_width = self.winfo_width() # detect current window size detected_width = self.winfo_width() # detect current window size
detected_height = self.winfo_height() detected_height = self.winfo_height()
@ -94,7 +96,8 @@ class CTk(tkinter.Tk):
# force new dimensions on window by using min, max, and geometry # 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().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().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" + f"{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) # 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) self.after(400, self._set_scaled_min_max)
@ -251,11 +254,11 @@ class CTk(tkinter.Tk):
if "bg" in args[0]: if "bg" in args[0]:
self._fg_color = args[0]["bg"] self._fg_color = args[0]["bg"]
bg_changed = True bg_changed = True
args[0]["bg"] = ThemeManager.single_color(self.fg_color, self.appearance_mode) args[0]["bg"] = ThemeManager.single_color(self._fg_color, self._appearance_mode)
elif "background" in args[0]: elif "background" in args[0]:
self._fg_color = args[0]["background"] self._fg_color = args[0]["background"]
bg_changed = True bg_changed = True
args[0]["background"] = ThemeManager.single_color(self.fg_color, self.appearance_mode) args[0]["background"] = ThemeManager.single_color(self._fg_color, self._appearance_mode)
if bg_changed: if bg_changed:
from ..widgets.widget_base_class import CTkBaseClass from ..widgets.widget_base_class import CTkBaseClass

View File

@ -14,7 +14,7 @@ class App(customtkinter.CTk):
self.title("CustomTkinter complex_example.py") self.title("CustomTkinter complex_example.py")
self.geometry(f"{1100}x{580}") self.geometry(f"{1100}x{580}")
self.minsize(800, 400) self.minsize(800, 400)
self.maxsize(1300, 700) #self.maxsize(1200, 700)
self.protocol("WM_DELETE_WINDOW", self.on_closing) # call .on_closing() when app gets closed self.protocol("WM_DELETE_WINDOW", self.on_closing) # call .on_closing() when app gets closed
# configure grid layout (4x4) # configure grid layout (4x4)
@ -23,7 +23,7 @@ class App(customtkinter.CTk):
self.grid_rowconfigure((0, 1, 2), weight=1) self.grid_rowconfigure((0, 1, 2), weight=1)
# create sidebar frame with widgets # create sidebar frame with widgets
self.sidebar_frame = customtkinter.CTkFrame(self, width=140) self.sidebar_frame = customtkinter.CTkFrame(self, width=140, corner_radius=0)
self.sidebar_frame.grid(row=0, column=0, rowspan=4, sticky="nsew") self.sidebar_frame.grid(row=0, column=0, rowspan=4, sticky="nsew")
self.sidebar_frame.grid_rowconfigure(4, weight=1) self.sidebar_frame.grid_rowconfigure(4, weight=1)
self.logo_label = customtkinter.CTkLabel(self.sidebar_frame, text="CustomTkinter", font=("Roboto", -16)) self.logo_label = customtkinter.CTkLabel(self.sidebar_frame, text="CustomTkinter", font=("Roboto", -16))

View File

@ -2,7 +2,7 @@ import customtkinter
app = customtkinter.CTk() app = customtkinter.CTk()
tabview_1 = customtkinter._CTkTabview(app, state="disabled") tabview_1 = customtkinter._CTkTabview(app)
tabview_1.pack(padx=20, pady=20) tabview_1.pack(padx=20, pady=20)
tab_1 = tabview_1.add("tab 1") tab_1 = tabview_1.add("tab 1")