From 9ffe61dd54aa70fc26894f6cde987b679832fbc8 Mon Sep 17 00:00:00 2001 From: TomSchimansky Date: Sat, 15 Oct 2022 01:02:54 +0200 Subject: [PATCH] added no_color_updates option for set_scaling methods, added transparency effect on Windows when window scaling, changed scaling loop times --- customtkinter/scaling_tracker.py | 23 ++++++-- customtkinter/widgets/ctk_button.py | 8 +-- customtkinter/widgets/ctk_checkbox.py | 2 +- customtkinter/widgets/ctk_combobox.py | 2 +- customtkinter/widgets/ctk_entry.py | 2 +- customtkinter/widgets/ctk_frame.py | 2 +- customtkinter/widgets/ctk_label.py | 2 +- customtkinter/widgets/ctk_optionmenu.py | 2 +- customtkinter/widgets/ctk_progressbar.py | 2 +- customtkinter/widgets/ctk_radiobutton.py | 2 +- customtkinter/widgets/ctk_slider.py | 2 +- customtkinter/widgets/ctk_switch.py | 2 +- customtkinter/widgets/ctk_tabview.py | 2 +- customtkinter/widgets/ctk_textbox.py | 2 +- customtkinter/widgets/dropdown_menu.py | 57 +++++++++---------- customtkinter/windows/ctk_tk.py | 9 ++- .../complex_example_new.py | 4 +- test/manual_integration_tests/test_tabview.py | 2 +- 18 files changed, 70 insertions(+), 57 deletions(-) diff --git a/customtkinter/scaling_tracker.py b/customtkinter/scaling_tracker.py index 2e9983a..e522cd7 100644 --- a/customtkinter/scaling_tracker.py +++ b/customtkinter/scaling_tracker.py @@ -1,6 +1,6 @@ import tkinter import sys -from typing import Callable +from typing import Callable, Dict class ScalingTracker: @@ -14,7 +14,8 @@ class ScalingTracker: spacing_scaling = 1 update_loop_running = False - update_loop_interval = 150 # milliseconds + update_loop_interval = 600 # ms + loop_pause_after_new_scaling = 1000 # ms @classmethod def get_widget_scaling(cls, widget) -> float: @@ -162,18 +163,32 @@ class ScalingTracker: @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(): + 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: - 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 except Exception: continue diff --git a/customtkinter/widgets/ctk_button.py b/customtkinter/widgets/ctk_button.py index 099f18e..427ca82 100644 --- a/customtkinter/widgets/ctk_button.py +++ b/customtkinter/widgets/ctk_button.py @@ -106,15 +106,11 @@ class CTkButton(CTkBaseClass): super()._set_scaling(*args, **kwargs) if self._text_label is not None: - self._text_label.destroy() - self._text_label = None - if self._image_label is not None: - self._image_label.destroy() - self._image_label = None + self._text_label.configure(font=self._apply_font_scaling(self._font)) 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: int = None, height: int = None): super()._set_dimensions(width, height) diff --git a/customtkinter/widgets/ctk_checkbox.py b/customtkinter/widgets/ctk_checkbox.py index fcfa56e..dbc6087 100644 --- a/customtkinter/widgets/ctk_checkbox.py +++ b/customtkinter/widgets/ctk_checkbox.py @@ -137,7 +137,7 @@ class CTkCheckBox(CTkBaseClass): height=self._apply_widget_scaling(self._desired_height)) self._canvas.configure(width=self._apply_widget_scaling(self._checkbox_width), 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): super()._set_dimensions(width, height) diff --git a/customtkinter/widgets/ctk_combobox.py b/customtkinter/widgets/ctk_combobox.py index d09fc39..6ce4604 100644 --- a/customtkinter/widgets/ctk_combobox.py +++ b/customtkinter/widgets/ctk_combobox.py @@ -135,7 +135,7 @@ class CTkComboBox(CTkBaseClass): 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: int = None, height: int = None): super()._set_dimensions(width, height) diff --git a/customtkinter/widgets/ctk_entry.py b/customtkinter/widgets/ctk_entry.py index 282bf66..b98d249 100644 --- a/customtkinter/widgets/ctk_entry.py +++ b/customtkinter/widgets/ctk_entry.py @@ -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)) 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): super()._set_dimensions(width, height) diff --git a/customtkinter/widgets/ctk_frame.py b/customtkinter/widgets/ctk_frame.py index b8b54c3..a452e57 100644 --- a/customtkinter/widgets/ctk_frame.py +++ b/customtkinter/widgets/ctk_frame.py @@ -62,7 +62,7 @@ class CTkFrame(CTkBaseClass): self._draw_engine = DrawEngine(self._canvas) self._overwrite_preferred_drawing_method = overwrite_preferred_drawing_method - self._draw() + self._draw(no_color_updates=True) def winfo_children(self) -> List[any]: """ diff --git a/customtkinter/widgets/ctk_label.py b/customtkinter/widgets/ctk_label.py index 05d6b4a..72e6940 100644 --- a/customtkinter/widgets/ctk_label.py +++ b/customtkinter/widgets/ctk_label.py @@ -88,7 +88,7 @@ class CTkLabel(CTkBaseClass): 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)))) - self._draw() + self._draw(no_color_updates=True) def _set_dimensions(self, width=None, height=None): super()._set_dimensions(width, height) diff --git a/customtkinter/widgets/ctk_optionmenu.py b/customtkinter/widgets/ctk_optionmenu.py index 89d6350..1b0e8d8 100644 --- a/customtkinter/widgets/ctk_optionmenu.py +++ b/customtkinter/widgets/ctk_optionmenu.py @@ -148,7 +148,7 @@ class CTkOptionMenu(CTkBaseClass): 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: int = None, height: int = None): super()._set_dimensions(width, height) diff --git a/customtkinter/widgets/ctk_progressbar.py b/customtkinter/widgets/ctk_progressbar.py index 6bdb783..783e360 100644 --- a/customtkinter/widgets/ctk_progressbar.py +++ b/customtkinter/widgets/ctk_progressbar.py @@ -94,7 +94,7 @@ class CTkProgressBar(CTkBaseClass): 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): super()._set_dimensions(width, height) diff --git a/customtkinter/widgets/ctk_radiobutton.py b/customtkinter/widgets/ctk_radiobutton.py index 3e654e2..a67ddb3 100644 --- a/customtkinter/widgets/ctk_radiobutton.py +++ b/customtkinter/widgets/ctk_radiobutton.py @@ -132,7 +132,7 @@ class CTkRadioButton(CTkBaseClass): height=self._apply_widget_scaling(self._desired_height)) self._canvas.configure(width=self._apply_widget_scaling(self._radiobutton_width), 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): super()._set_dimensions(width, height) diff --git a/customtkinter/widgets/ctk_slider.py b/customtkinter/widgets/ctk_slider.py index 27b8c65..201735c 100644 --- a/customtkinter/widgets/ctk_slider.py +++ b/customtkinter/widgets/ctk_slider.py @@ -114,7 +114,7 @@ class CTkSlider(CTkBaseClass): 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): super()._set_dimensions(width, height) diff --git a/customtkinter/widgets/ctk_switch.py b/customtkinter/widgets/ctk_switch.py index 410846c..9c4c31c 100644 --- a/customtkinter/widgets/ctk_switch.py +++ b/customtkinter/widgets/ctk_switch.py @@ -137,7 +137,7 @@ class CTkSwitch(CTkBaseClass): height=self._apply_widget_scaling(self._desired_height)) self._canvas.configure(width=self._apply_widget_scaling(self._switch_width), 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): super()._set_dimensions(width, height) diff --git a/customtkinter/widgets/ctk_tabview.py b/customtkinter/widgets/ctk_tabview.py index 24e2c1a..b1f8632 100644 --- a/customtkinter/widgets/ctk_tabview.py +++ b/customtkinter/widgets/ctk_tabview.py @@ -123,7 +123,7 @@ class CTkTabview(CTkBaseClass): 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)) - self._draw() + self._draw(no_color_updates=True) def _set_dimensions(self, width=None, height=None): super()._set_dimensions(width, height) diff --git a/customtkinter/widgets/ctk_textbox.py b/customtkinter/widgets/ctk_textbox.py index 6647eef..1a09083 100644 --- a/customtkinter/widgets/ctk_textbox.py +++ b/customtkinter/widgets/ctk_textbox.py @@ -182,7 +182,7 @@ class CTkTextbox(CTkBaseClass): self._canvas.configure(width=self._apply_widget_scaling(self._desired_width), 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._draw() + self._draw(no_color_updates=True) def _set_dimensions(self, width=None, height=None): super()._set_dimensions(width, height) diff --git a/customtkinter/widgets/dropdown_menu.py b/customtkinter/widgets/dropdown_menu.py index f97d0c2..a3edb49 100644 --- a/customtkinter/widgets/dropdown_menu.py +++ b/customtkinter/widgets/dropdown_menu.py @@ -48,32 +48,31 @@ class DropdownMenu(tkinter.Menu): """ apply platform specific appearance attributes, configure all colors """ if sys.platform == "darwin": - self.configure(tearoff=False, - font=self._apply_font_scaling(self._font)) + super().configure(tearoff=False, + font=self._apply_font_scaling(self._font)) elif sys.platform.startswith("win"): - print("dropdon win") - self.configure(tearoff=False, - relief="flat", - activebackground=ThemeManager.single_color(self._hover_color, self._appearance_mode), - borderwidth=0, - activeborderwidth=self._apply_widget_scaling(4), - bg=ThemeManager.single_color(self._fg_color, self._appearance_mode), - fg=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), - cursor="hand2") + super().configure(tearoff=False, + relief="flat", + activebackground=ThemeManager.single_color(self._hover_color, self._appearance_mode), + borderwidth=self._apply_widget_scaling(4), + activeborderwidth=self._apply_widget_scaling(4), + bg=ThemeManager.single_color(self._fg_color, self._appearance_mode), + fg=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), + cursor="hand2") else: - self.configure(tearoff=False, - relief="flat", - activebackground=ThemeManager.single_color(self._hover_color, self._appearance_mode), - borderwidth=0, - activeborderwidth=0, - bg=ThemeManager.single_color(self._fg_color, self._appearance_mode), - fg=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)) + super().configure(tearoff=False, + relief="flat", + activebackground=ThemeManager.single_color(self._hover_color, self._appearance_mode), + borderwidth=0, + activeborderwidth=0, + bg=ThemeManager.single_color(self._fg_color, self._appearance_mode), + fg=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)) def _add_menu_commands(self): """ 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): if "fg_color" in kwargs: 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: 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: 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: self._font = kwargs.pop("font") @@ -151,6 +150,9 @@ class DropdownMenu(tkinter.Menu): elif attribute_name == "values": return self._values + else: + return super().cget(attribute_name) + def _apply_widget_scaling(self, value: Union[int, float, str]) -> Union[float, str]: if isinstance(value, (int, float)): return value * self._widget_scaling @@ -183,10 +185,7 @@ class DropdownMenu(tkinter.Menu): self._widget_scaling = new_widget_scaling self._spacing_scaling = new_spacing_scaling - super().configure(font=self._apply_font_scaling(self._font)) - - if sys.platform.startswith("win"): - self.configure(activeborderwidth=self._apply_widget_scaling(4)) + self._configure_menu_for_platforms() def _set_appearance_mode(self, mode_string): """ colors won't update on appearance mode change when dropdown is open, because it's not necessary """ diff --git a/customtkinter/windows/ctk_tk.py b/customtkinter/windows/ctk_tk.py index 599f18f..efa7818 100644 --- a/customtkinter/windows/ctk_tk.py +++ b/customtkinter/windows/ctk_tk.py @@ -5,6 +5,7 @@ import os import platform import ctypes import re +import time from typing import Union, Tuple from ..appearance_mode_tracker import AppearanceModeTracker @@ -78,6 +79,7 @@ class CTk(tkinter.Tk): def _update_dimensions_event(self, event=None): if not self._block_update_dimensions_event: + self.update_idletasks() detected_width = self.winfo_width() # detect current window size detected_height = self.winfo_height() @@ -94,7 +96,8 @@ class CTk(tkinter.Tk): # 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" + 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) self.after(400, self._set_scaled_min_max) @@ -251,11 +254,11 @@ class CTk(tkinter.Tk): if "bg" in args[0]: self._fg_color = args[0]["bg"] 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]: self._fg_color = args[0]["background"] 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: from ..widgets.widget_base_class import CTkBaseClass diff --git a/test/manual_integration_tests/complex_example_new.py b/test/manual_integration_tests/complex_example_new.py index 14b2ebe..ae27408 100644 --- a/test/manual_integration_tests/complex_example_new.py +++ b/test/manual_integration_tests/complex_example_new.py @@ -14,7 +14,7 @@ class App(customtkinter.CTk): self.title("CustomTkinter complex_example.py") self.geometry(f"{1100}x{580}") 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 # configure grid layout (4x4) @@ -23,7 +23,7 @@ class App(customtkinter.CTk): self.grid_rowconfigure((0, 1, 2), weight=1) # 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_rowconfigure(4, weight=1) self.logo_label = customtkinter.CTkLabel(self.sidebar_frame, text="CustomTkinter", font=("Roboto", -16)) diff --git a/test/manual_integration_tests/test_tabview.py b/test/manual_integration_tests/test_tabview.py index cfd9e7b..d9c32f6 100644 --- a/test/manual_integration_tests/test_tabview.py +++ b/test/manual_integration_tests/test_tabview.py @@ -2,7 +2,7 @@ import customtkinter app = customtkinter.CTk() -tabview_1 = customtkinter._CTkTabview(app, state="disabled") +tabview_1 = customtkinter._CTkTabview(app) tabview_1.pack(padx=20, pady=20) tab_1 = tabview_1.add("tab 1")