diff --git a/CHANGELOG.md b/CHANGELOG.md index 1853ac1..f3246e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ ToDo: - image tuple for light/dark mode - change font attribute in wiki - add new button attributes to wiki + - cursor configuring + - overwrite winfo methods ## Unreleased - 2022-10-2 ### Added diff --git a/customtkinter/__init__.py b/customtkinter/__init__.py index 49d01f8..c3cca8f 100644 --- a/customtkinter/__init__.py +++ b/customtkinter/__init__.py @@ -4,11 +4,11 @@ import os import sys # import manager classes -from .appearance_mode_tracker import AppearanceModeTracker -from .widgets.font.font_manager import FontManager -from .scaling_tracker import ScalingTracker -from .theme_manager import ThemeManager -from .widgets.core_rendering.draw_engine import DrawEngine +from .windows.widgets.appearance_mode.appearance_mode_tracker import AppearanceModeTracker +from .windows.widgets.font.font_manager import FontManager +from .windows.widgets.scaling.scaling_tracker import ScalingTracker +from .windows.widgets.theme.theme_manager import ThemeManager +from .windows.widgets.core_rendering.draw_engine import DrawEngine AppearanceModeTracker.init_appearance_mode() @@ -46,23 +46,21 @@ if FontManager.load_font(os.path.join(script_directory, "assets", "fonts", "Cust DrawEngine.preferred_drawing_method = "circle_shapes" # import widgets -from customtkinter.widgets.core_widget_classes.widget_base_class import CTkBaseClass -from .widgets.ctk_button import CTkButton -from .widgets.ctk_checkbox import CTkCheckBox -from .widgets.ctk_entry import CTkEntry -from .widgets.ctk_slider import CTkSlider -from .widgets.ctk_frame import CTkFrame -from .widgets.ctk_progressbar import CTkProgressBar -from .widgets.ctk_label import CTkLabel -from .widgets.ctk_radiobutton import CTkRadioButton -from .widgets.core_rendering.ctk_canvas import CTkCanvas -from .widgets.ctk_switch import CTkSwitch -from .widgets.ctk_optionmenu import CTkOptionMenu -from .widgets.ctk_combobox import CTkComboBox -from .widgets.ctk_scrollbar import CTkScrollbar -from .widgets.ctk_textbox import CTkTextbox -from .widgets.ctk_tabview import CTkTabview -from .widgets.ctk_segmented_button import CTkSegmentedButton +from .windows.widgets.ctk_button import CTkButton +from .windows.widgets.ctk_checkbox import CTkCheckBox +from .windows.widgets.ctk_combobox import CTkComboBox +from .windows.widgets.ctk_entry import CTkEntry +from .windows.widgets.ctk_frame import CTkFrame +from .windows.widgets.ctk_label import CTkLabel +from .windows.widgets.ctk_optionmenu import CTkOptionMenu +from .windows.widgets.ctk_progressbar import CTkProgressBar +from .windows.widgets.ctk_radiobutton import CTkRadioButton +from .windows.widgets.ctk_scrollbar import CTkScrollbar +from .windows.widgets.ctk_segmented_button import CTkSegmentedButton +from .windows.widgets.ctk_slider import CTkSlider +from .windows.widgets.ctk_switch import CTkSwitch +from .windows.widgets.ctk_tabview import CTkTabview +from .windows.widgets.ctk_textbox import CTkTextbox # import windows from .windows.ctk_tk import CTk @@ -70,7 +68,7 @@ from .windows.ctk_toplevel import CTkToplevel from .windows.ctk_input_dialog import CTkInputDialog # font classes -from .widgets.font.ctk_font import CTkFont +from .windows.widgets.font.ctk_font import CTkFont def set_appearance_mode(mode_string: str): diff --git a/customtkinter/widgets/__init__.py b/customtkinter/widgets/__init__.py deleted file mode 100644 index 517b716..0000000 --- a/customtkinter/widgets/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from customtkinter.widgets.core_rendering.ctk_canvas import CTkCanvas - -CTkCanvas.init_font_character_mapping() diff --git a/customtkinter/windows/ctk_input_dialog.py b/customtkinter/windows/ctk_input_dialog.py index 2f1e72c..2c2c002 100644 --- a/customtkinter/windows/ctk_input_dialog.py +++ b/customtkinter/windows/ctk_input_dialog.py @@ -1,11 +1,11 @@ from typing import Union, Tuple -from ..widgets.ctk_label import CTkLabel -from ..widgets.ctk_entry import CTkEntry -from ..windows.ctk_toplevel import CTkToplevel -from ..widgets.ctk_button import CTkButton -from ..appearance_mode_tracker import AppearanceModeTracker -from ..theme_manager import ThemeManager +from .widgets.ctk_label import CTkLabel +from .widgets.ctk_entry import CTkEntry +from .ctk_toplevel import CTkToplevel +from .widgets.ctk_button import CTkButton +from .widgets.appearance_mode.appearance_mode_tracker import AppearanceModeTracker +from .widgets.theme.theme_manager import ThemeManager class CTkInputDialog: diff --git a/customtkinter/windows/ctk_tk.py b/customtkinter/windows/ctk_tk.py index d7f14b8..2663e80 100644 --- a/customtkinter/windows/ctk_tk.py +++ b/customtkinter/windows/ctk_tk.py @@ -4,17 +4,16 @@ import sys import os import platform import ctypes -import re -from typing import Union, Tuple, List +from typing import Union, Tuple -from ..appearance_mode_tracker import AppearanceModeTracker -from ..theme_manager import ThemeManager -from ..scaling_tracker import ScalingTracker +from .widgets.theme.theme_manager import ThemeManager +from .widgets.scaling.scaling_base_class import CTkScalingBaseClass +from .widgets.appearance_mode.appearance_mode_base_class import CTkAppearanceModeBaseClass from ..utility.utility_functions import pop_from_dict_by_set, check_kwargs_empty -class CTk(tkinter.Tk): +class CTk(tkinter.Tk, CTkAppearanceModeBaseClass, CTkScalingBaseClass): """ Main app window with dark titlebar on Windows and macOS. For detailed information check out the documentation. @@ -33,21 +32,15 @@ class CTk(tkinter.Tk): fg_color: Union[str, Tuple[str, str]] = "default_theme", **kwargs): - ScalingTracker.activate_high_dpi_awareness() # make process DPI aware self._enable_macos_dark_title_bar() - super().__init__(**pop_from_dict_by_set(kwargs, self._valid_tk_constructor_arguments)) + # call init methods of super classes + tkinter.Tk.__init__(self, **pop_from_dict_by_set(kwargs, self._valid_tk_constructor_arguments)) + CTkAppearanceModeBaseClass.__init__(self) + CTkScalingBaseClass.__init__(self, scaling_type="window") check_kwargs_empty(kwargs, raise_error=True) - # add set_appearance_mode method to callback list of AppearanceModeTracker for appearance mode changes - AppearanceModeTracker.add(self._set_appearance_mode, self) - self._appearance_mode = AppearanceModeTracker.get_mode() # 0: "Light" 1: "Dark" - - # add set_scaling method to callback list of ScalingTracker for automatic scaling changes - ScalingTracker.add_widget(self._set_scaling, self) - self._window_scaling = ScalingTracker.get_window_scaling(self) - - self._current_width = 600 # initial window size, always without scaling + self._current_width = 600 # initial window size, independent of scaling self._current_height = 500 self._min_width: int = 0 self._min_height: int = 0 @@ -57,8 +50,11 @@ class CTk(tkinter.Tk): self._fg_color = ThemeManager.theme["color"]["window_bg_color"] if fg_color == "default_theme" else fg_color + # set bg of tkinter.Tk super().configure(bg=self._apply_appearance_mode(self._fg_color)) - super().title("CTk") + + # set title and initial geometry + self.title("CTk") self.geometry(f"{self._current_width}x{self._current_height}") self._state_before_windows_set_titlebar_color = None @@ -77,6 +73,14 @@ class CTk(tkinter.Tk): self._block_update_dimensions_event = False + def destroy(self): + self._disable_macos_dark_title_bar() + + # call destroy methods of super classes + tkinter.Tk.destroy(self) + CTkAppearanceModeBaseClass.destroy(self) + CTkScalingBaseClass.destroy(self) + def _focus_in_event(self, event): # sometimes window looses jumps back on macOS if window is selected from Mission Control, so has to be lifted again if sys.platform == "darwin": @@ -123,12 +127,6 @@ class CTk(tkinter.Tk): if self._max_width is not None or self._max_height is not None: super().maxsize(self._apply_window_scaling(self._max_width), self._apply_window_scaling(self._max_height)) - def destroy(self): - AppearanceModeTracker.remove(self._set_appearance_mode) - ScalingTracker.remove_window(self._set_scaling, self) - self._disable_macos_dark_title_bar() - super().destroy() - def withdraw(self): if self._window_exists is False: self._withdraw_called_before_window_exists = True @@ -201,59 +199,6 @@ class CTk(tkinter.Tk): else: return self._reverse_geometry_scaling(super().geometry()) - @staticmethod - def _parse_geometry_string(geometry_string: str) -> tuple: - # index: 1 2 3 4 5 6 - # regex group structure: ('x', '', '', '+-+-', '-', '-') - result = re.search(r"((\d+)x(\d+)){0,1}(\+{0,1}([+-]{0,1}\d+)\+{0,1}([+-]{0,1}\d+)){0,1}", geometry_string) - - width = int(result.group(2)) if result.group(2) is not None else None - height = int(result.group(3)) if result.group(3) is not None else None - x = int(result.group(5)) if result.group(5) is not None else None - y = int(result.group(6)) if result.group(6) is not None else None - - return width, height, x, y - - def _apply_geometry_scaling(self, geometry_string: str) -> str: - width, height, x, y = self._parse_geometry_string(geometry_string) - - if x is None and y is None: # no and in geometry_string - return f"{round(width * self._window_scaling)}x{round(height * self._window_scaling)}" - - elif width is None and height is None: # no and in geometry_string - return f"+{x}+{y}" - - else: - return f"{round(width * self._window_scaling)}x{round(height * self._window_scaling)}+{x}+{y}" - - def _reverse_geometry_scaling(self, scaled_geometry_string: str) -> str: - width, height, x, y = self._parse_geometry_string(scaled_geometry_string) - - if x is None and y is None: # no and in geometry_string - return f"{round(width / self._window_scaling)}x{round(height / self._window_scaling)}" - - elif width is None and height is None: # no and in geometry_string - return f"+{x}+{y}" - - else: - return f"{round(width / self._window_scaling)}x{round(height / self._window_scaling)}+{x}+{y}" - - def _apply_window_scaling(self, value): - if isinstance(value, (int, float)): - return int(value * self._window_scaling) - else: - return value - - def _apply_appearance_mode(self, color: Union[str, Tuple[str, str], List[str]]) -> str: - """ color can be either a single hex color string or a color name or it can be a - tuple color with (light_color, dark_color). The functions returns - always a single color string """ - - if type(color) == tuple or type(color) == list: - return color[self._appearance_mode] - else: - return color - def configure(self, **kwargs): if "fg_color" in kwargs: self._fg_color = kwargs.pop("fg_color") diff --git a/customtkinter/windows/ctk_toplevel.py b/customtkinter/windows/ctk_toplevel.py index 6a9ba84..bae427d 100644 --- a/customtkinter/windows/ctk_toplevel.py +++ b/customtkinter/windows/ctk_toplevel.py @@ -4,17 +4,16 @@ import sys import os import platform import ctypes -import re -from typing import Union, Tuple, List +from typing import Union, Tuple -from ..appearance_mode_tracker import AppearanceModeTracker -from ..theme_manager import ThemeManager -from ..scaling_tracker import ScalingTracker +from .widgets.theme.theme_manager import ThemeManager +from .widgets.scaling.scaling_base_class import CTkScalingBaseClass +from .widgets.appearance_mode.appearance_mode_base_class import CTkAppearanceModeBaseClass from ..utility.utility_functions import pop_from_dict_by_set, check_kwargs_empty -class CTkToplevel(tkinter.Toplevel): +class CTkToplevel(tkinter.Toplevel, CTkAppearanceModeBaseClass, CTkScalingBaseClass): """ Toplevel window with dark titlebar on Windows and macOS. For detailed information check out the documentation. @@ -33,17 +32,12 @@ class CTkToplevel(tkinter.Toplevel): self._enable_macos_dark_title_bar() + # call init methods of super classees super().__init__(*args, **pop_from_dict_by_set(kwargs, self._valid_tk_toplevel_arguments)) + CTkAppearanceModeBaseClass.__init__(self) + CTkScalingBaseClass.__init__(self, scaling_type="window") check_kwargs_empty(kwargs, raise_error=True) - # add set_appearance_mode method to callback list of AppearanceModeTracker for appearance mode changes - AppearanceModeTracker.add(self._set_appearance_mode, self) - self._appearance_mode = AppearanceModeTracker.get_mode() # 0: "Light" 1: "Dark" - - # add set_scaling method to callback list of ScalingTracker for automatic scaling changes - ScalingTracker.add_widget(self._set_scaling, self) - self._window_scaling = ScalingTracker.get_window_scaling(self) - self._current_width = 200 # initial window size, always without scaling self._current_height = 200 self._min_width: int = 0 @@ -54,7 +48,10 @@ class CTkToplevel(tkinter.Toplevel): self._fg_color = ThemeManager.theme["color"]["window_bg_color"] if fg_color == "default_theme" else fg_color + # set bg color of tkinter.Toplevel super().configure(bg=self._apply_appearance_mode(self._fg_color)) + + # set title of tkinter.Toplevel super().title("CTkToplevel") self._state_before_windows_set_titlebar_color = None @@ -71,6 +68,14 @@ class CTkToplevel(tkinter.Toplevel): self.bind('', self._update_dimensions_event) self.bind('', self._focus_in_event) + def destroy(self): + self._disable_macos_dark_title_bar() + + # call destroy methods of super classes + tkinter.Toplevel.destroy(self) + CTkAppearanceModeBaseClass.destroy(self) + CTkScalingBaseClass.destroy(self) + def _focus_in_event(self, event): # sometimes window looses jumps back on macOS if window is selected from Mission Control, so has to be lifted again if sys.platform == "darwin": @@ -114,65 +119,6 @@ class CTkToplevel(tkinter.Toplevel): else: return self._reverse_geometry_scaling(super().geometry()) - @staticmethod - def _parse_geometry_string(geometry_string: str) -> tuple: - # index: 1 2 3 4 5 6 - # regex group structure: ('x', '', '', '+-+-', '-', '-') - result = re.search(r"((\d+)x(\d+)){0,1}(\+{0,1}([+-]{0,1}\d+)\+{0,1}([+-]{0,1}\d+)){0,1}", geometry_string) - - width = int(result.group(2)) if result.group(2) is not None else None - height = int(result.group(3)) if result.group(3) is not None else None - x = int(result.group(5)) if result.group(5) is not None else None - y = int(result.group(6)) if result.group(6) is not None else None - - return width, height, x, y - - def _apply_geometry_scaling(self, geometry_string: str) -> str: - width, height, x, y = self._parse_geometry_string(geometry_string) - - if x is None and y is None: # no and in geometry_string - return f"{round(width * self._window_scaling)}x{round(height * self._window_scaling)}" - - elif width is None and height is None: # no and in geometry_string - return f"+{x}+{y}" - - else: - return f"{round(width * self._window_scaling)}x{round(height * self._window_scaling)}+{x}+{y}" - - def _reverse_geometry_scaling(self, scaled_geometry_string: str) -> str: - width, height, x, y = self._parse_geometry_string(scaled_geometry_string) - - if x is None and y is None: # no and in geometry_string - return f"{round(width / self._window_scaling)}x{round(height / self._window_scaling)}" - - elif width is None and height is None: # no and in geometry_string - return f"+{x}+{y}" - - else: - return f"{round(width / self._window_scaling)}x{round(height / self._window_scaling)}+{x}+{y}" - - def _apply_window_scaling(self, value): - if isinstance(value, (int, float)): - return int(value * self._window_scaling) - else: - return value - - def _apply_appearance_mode(self, color: Union[str, Tuple[str, str], List[str]]) -> str: - """ color can be either a single hex color string or a color name or it can be a - tuple color with (light_color, dark_color). The functions returns - always a single color string """ - - if type(color) == tuple or type(color) == list: - return color[self._appearance_mode] - else: - return color - - def destroy(self): - AppearanceModeTracker.remove(self._set_appearance_mode) - ScalingTracker.remove_window(self._set_scaling, self) - self._disable_macos_dark_title_bar() - super().destroy() - def withdraw(self): if self._windows_set_titlebar_color_called: self._withdraw_called_after_windows_set_titlebar_color = True diff --git a/customtkinter/windows/widgets/__init__.py b/customtkinter/windows/widgets/__init__.py new file mode 100644 index 0000000..7b8ad04 --- /dev/null +++ b/customtkinter/windows/widgets/__init__.py @@ -0,0 +1,3 @@ +from customtkinter.windows.widgets.core_rendering.ctk_canvas import CTkCanvas + +CTkCanvas.init_font_character_mapping() diff --git a/customtkinter/widgets/core_rendering/__init__.py b/customtkinter/windows/widgets/appearance_mode/__init__.py similarity index 100% rename from customtkinter/widgets/core_rendering/__init__.py rename to customtkinter/windows/widgets/appearance_mode/__init__.py diff --git a/customtkinter/widgets/core_widget_classes/appearance_mode_base_class.py b/customtkinter/windows/widgets/appearance_mode/appearance_mode_base_class.py similarity index 52% rename from customtkinter/widgets/core_widget_classes/appearance_mode_base_class.py rename to customtkinter/windows/widgets/appearance_mode/appearance_mode_base_class.py index d3c6857..8db5978 100644 --- a/customtkinter/widgets/core_widget_classes/appearance_mode_base_class.py +++ b/customtkinter/windows/widgets/appearance_mode/appearance_mode_base_class.py @@ -1,8 +1,16 @@ from typing import Union, Tuple, List +from abc import ABC, abstractmethod + +from .appearance_mode_tracker import AppearanceModeTracker -class CTkAppearanceModeBaseClass: +class CTkAppearanceModeBaseClass(ABC): def __init__(self): + AppearanceModeTracker.add(self._set_appearance_mode, self) + self._appearance_mode = AppearanceModeTracker.get_mode() # 0: "Light" 1: "Dark" + + def destroy(self): + AppearanceModeTracker.remove(self._set_appearance_mode) def _apply_appearance_mode(self, color: Union[str, Tuple[str, str], List[str]]) -> str: """ color can be either a single hex color string or a color name or it can be a @@ -13,3 +21,7 @@ class CTkAppearanceModeBaseClass: return color[self._appearance_mode] else: return color + + @abstractmethod + def _set_appearance_mode(self, mode_string: str): + return diff --git a/customtkinter/appearance_mode_tracker.py b/customtkinter/windows/widgets/appearance_mode/appearance_mode_tracker.py similarity index 100% rename from customtkinter/appearance_mode_tracker.py rename to customtkinter/windows/widgets/appearance_mode/appearance_mode_tracker.py diff --git a/customtkinter/widgets/core_widget_classes/__init__.py b/customtkinter/windows/widgets/core_rendering/__init__.py similarity index 100% rename from customtkinter/widgets/core_widget_classes/__init__.py rename to customtkinter/windows/widgets/core_rendering/__init__.py diff --git a/customtkinter/widgets/core_rendering/ctk_canvas.py b/customtkinter/windows/widgets/core_rendering/ctk_canvas.py similarity index 100% rename from customtkinter/widgets/core_rendering/ctk_canvas.py rename to customtkinter/windows/widgets/core_rendering/ctk_canvas.py diff --git a/customtkinter/widgets/core_rendering/draw_engine.py b/customtkinter/windows/widgets/core_rendering/draw_engine.py similarity index 99% rename from customtkinter/widgets/core_rendering/draw_engine.py rename to customtkinter/windows/widgets/core_rendering/draw_engine.py index d0cc0eb..4c0a336 100644 --- a/customtkinter/widgets/core_rendering/draw_engine.py +++ b/customtkinter/windows/widgets/core_rendering/draw_engine.py @@ -5,7 +5,7 @@ import tkinter from typing import Union, TYPE_CHECKING if TYPE_CHECKING: - from customtkinter.widgets.core_rendering.ctk_canvas import CTkCanvas + from customtkinter.windows.widgets.core_rendering.ctk_canvas import CTkCanvas class DrawEngine: diff --git a/customtkinter/widgets/font/__init__.py b/customtkinter/windows/widgets/core_widget_classes/__init__.py similarity index 100% rename from customtkinter/widgets/font/__init__.py rename to customtkinter/windows/widgets/core_widget_classes/__init__.py diff --git a/customtkinter/widgets/core_widget_classes/dropdown_menu.py b/customtkinter/windows/widgets/core_widget_classes/dropdown_menu.py similarity index 94% rename from customtkinter/widgets/core_widget_classes/dropdown_menu.py rename to customtkinter/windows/widgets/core_widget_classes/dropdown_menu.py index 4254ea8..52a6889 100644 --- a/customtkinter/widgets/core_widget_classes/dropdown_menu.py +++ b/customtkinter/windows/widgets/core_widget_classes/dropdown_menu.py @@ -2,13 +2,14 @@ import tkinter import sys from typing import Union, Tuple, Callable, List -from ...theme_manager import ThemeManager -from ...appearance_mode_tracker import AppearanceModeTracker -from ...scaling_tracker import ScalingTracker +from ..theme.theme_manager import ThemeManager +from ..scaling.scaling_tracker import ScalingTracker from ..font.ctk_font import CTkFont +from ..appearance_mode.appearance_mode_tracker import AppearanceModeTracker +from ..appearance_mode.appearance_mode_base_class import CTkAppearanceModeBaseClass -class DropdownMenu(tkinter.Menu): +class DropdownMenu(tkinter.Menu, CTkAppearanceModeBaseClass): def __init__(self, *args, min_character_width: int = 18, @@ -21,7 +22,9 @@ class DropdownMenu(tkinter.Menu): values: List[str] = None, **kwargs): - super().__init__(*args, **kwargs) + # call init methods of super classes + tkinter.Menu.__init__(self, *args, **kwargs) + CTkAppearanceModeBaseClass.__init__(self) ScalingTracker.add_widget(self._set_scaling, self) self._widget_scaling = ScalingTracker.get_widget_scaling(self) @@ -46,14 +49,17 @@ class DropdownMenu(tkinter.Menu): self._add_menu_commands() - def _update_font(self): - """ pass font to tkinter widgets with applied font scaling """ - super().configure(font=self._apply_font_scaling(self._font)) - def destroy(self): if isinstance(self._font, CTkFont): self._font.remove_size_configure_callback(self._update_font) - super().destroy() + + # call destroy methods of super classes + tkinter.Menu.destroy(self) + CTkAppearanceModeBaseClass.destroy(self) + + def _update_font(self): + """ pass font to tkinter widgets with applied font scaling """ + super().configure(font=self._apply_font_scaling(self._font)) def _configure_menu_for_platforms(self): """ apply platform specific appearance attributes, configure all colors """ diff --git a/customtkinter/widgets/core_widget_classes/widget_base_class.py b/customtkinter/windows/widgets/core_widget_classes/widget_base_class.py similarity index 76% rename from customtkinter/widgets/core_widget_classes/widget_base_class.py rename to customtkinter/windows/widgets/core_widget_classes/widget_base_class.py index 411d6df..82eef05 100644 --- a/customtkinter/widgets/core_widget_classes/widget_base_class.py +++ b/customtkinter/windows/widgets/core_widget_classes/widget_base_class.py @@ -1,26 +1,24 @@ import sys import tkinter import tkinter.ttk as ttk -import copy -from typing import Union, Callable, Tuple, List -from abc import ABC, abstractmethod +from typing import Union, Callable, Tuple try: from typing import TypedDict except ImportError: from typing_extensions import TypedDict -from ...windows.ctk_tk import CTk -from ...windows.ctk_toplevel import CTkToplevel -from ...appearance_mode_tracker import AppearanceModeTracker -from ...scaling_tracker import ScalingTracker -from ...theme_manager import ThemeManager +from ...ctk_tk import CTk +from ...ctk_toplevel import CTkToplevel +from ..theme.theme_manager import ThemeManager from ..font.ctk_font import CTkFont +from ..appearance_mode.appearance_mode_base_class import CTkAppearanceModeBaseClass +from ..scaling.scaling_base_class import CTkScalingBaseClass -from ...utility.utility_functions import pop_from_dict_by_set, check_kwargs_empty +from ....utility.utility_functions import pop_from_dict_by_set, check_kwargs_empty -class CTkBaseClass(tkinter.Frame, ABC): +class CTkBaseClass(tkinter.Frame, CTkAppearanceModeBaseClass, CTkScalingBaseClass): """ Base class of every CTk widget, handles the dimensions, _bg_color, appearance_mode changes, scaling, bg changes of master if master is not a CTk widget """ @@ -37,21 +35,21 @@ class CTkBaseClass(tkinter.Frame, ABC): bg_color: Union[str, Tuple[str, str], None] = None, **kwargs): - super().__init__(master=master, width=width, height=height, **pop_from_dict_by_set(kwargs, self._valid_tk_frame_attributes)) + # call init methods of super classes + tkinter.Frame.__init__(self, master=master, width=width, height=height, **pop_from_dict_by_set(kwargs, self._valid_tk_frame_attributes)) + CTkAppearanceModeBaseClass.__init__(self) + CTkScalingBaseClass.__init__(self, scaling_type="widget") # check if kwargs is empty, if not raise error for unsupported arguments check_kwargs_empty(kwargs, raise_error=True) - # dimensions + # dimensions independent of scaling self._current_width = width # _current_width and _current_height in pixel, represent current size of the widget self._current_height = height # _current_width and _current_height are independent of the scale self._desired_width = width # _desired_width and _desired_height, represent desired size set by width and height self._desired_height = height - # scaling - ScalingTracker.add_widget(self._set_scaling, self) # add callback for automatic scaling changes - self._widget_scaling = ScalingTracker.get_widget_scaling(self) - + # set width and height of tkinter.Frame super().configure(width=self._apply_widget_scaling(self._desired_width), height=self._apply_widget_scaling(self._desired_height)) @@ -59,17 +57,15 @@ class CTkBaseClass(tkinter.Frame, ABC): class GeometryCallDict(TypedDict): function: Callable kwargs: dict - self._last_geometry_manager_call: Union[GeometryCallDict, None] = None - # add set_appearance_mode method to callback list of AppearanceModeTracker for appearance mode changes - AppearanceModeTracker.add(self._set_appearance_mode, self) - self._appearance_mode = AppearanceModeTracker.get_mode() # 0: "Light" 1: "Dark" - # background color self._bg_color: Union[str, Tuple[str, str]] = self._detect_color_of_master() if bg_color is None else bg_color + # set bg color of tkinter.Frame super().configure(bg=self._apply_appearance_mode(self._bg_color)) + + # add configure callback to tkinter.Frame super().bind('', self._update_dimensions_event) # overwrite configure methods of master when master is tkinter widget, so that bg changes get applied on child CTk widget as well @@ -93,7 +89,14 @@ class CTkBaseClass(tkinter.Frame, ABC): self.master.config = new_configure self.master.configure = new_configure - @abstractmethod + def destroy(self): + """ Destroy this and all descendants widgets. """ + + # call destroy methods of super classes + tkinter.Frame.destroy(self) + CTkAppearanceModeBaseClass.destroy(self) + CTkScalingBaseClass.destroy(self) + def _draw(self, no_color_updates: bool = False): return @@ -221,66 +224,6 @@ class CTkBaseClass(tkinter.Frame, ABC): super().configure(width=self._apply_widget_scaling(self._desired_width), height=self._apply_widget_scaling(self._desired_height)) - def _apply_widget_scaling(self, value: Union[int, float, str]) -> Union[float, str]: - if isinstance(value, (int, float)): - return value * self._widget_scaling - else: - return value - - def _apply_font_scaling(self, font: Union[Tuple, CTkFont]) -> tuple: - """ Takes CTkFont object and returns tuple font with scaled size, has to be called again for every change of font object """ - if type(font) == tuple: - if len(font) == 1: - return font - elif len(font) == 2: - return font[0], -abs(round(font[1] * self._widget_scaling)) - elif len(font) == 3: - return font[0], -abs(round(font[1] * self._widget_scaling)), font[2] - else: - raise ValueError(f"Can not scale font {font}. font needs to be tuple of len 1, 2 or 3") - - elif isinstance(font, CTkFont): - return font.create_scaled_tuple(self._widget_scaling) - else: - raise ValueError(f"Can not scale font '{font}' of type {type(font)}. font needs to be tuple or instance of CTkFont") - - def _apply_argument_scaling(self, kwargs: dict) -> dict: - scaled_kwargs = copy.copy(kwargs) - - if "pady" in scaled_kwargs: - if isinstance(scaled_kwargs["pady"], (int, float, str)): - scaled_kwargs["pady"] = self._apply_widget_scaling(scaled_kwargs["pady"]) - elif isinstance(scaled_kwargs["pady"], tuple): - scaled_kwargs["pady"] = tuple([self._apply_widget_scaling(v) for v in scaled_kwargs["pady"]]) - if "padx" in kwargs: - if isinstance(scaled_kwargs["padx"], (int, float, str)): - scaled_kwargs["padx"] = self._apply_widget_scaling(scaled_kwargs["padx"]) - elif isinstance(scaled_kwargs["padx"], tuple): - scaled_kwargs["padx"] = tuple([self._apply_widget_scaling(v) for v in scaled_kwargs["padx"]]) - - if "x" in scaled_kwargs: - scaled_kwargs["x"] = self._apply_widget_scaling(scaled_kwargs["x"]) - if "y" in scaled_kwargs: - scaled_kwargs["y"] = self._apply_widget_scaling(scaled_kwargs["y"]) - - return scaled_kwargs - - def _apply_appearance_mode(self, color: Union[str, Tuple[str, str], List[str]]) -> str: - """ color can be either a single hex color string or a color name or it can be a - tuple color with (light_color, dark_color). The functions returns - always a single color string """ - - if type(color) == tuple or type(color) == list: - return color[self._appearance_mode] - else: - return color - - def destroy(self): - """ Destroy this and all descendants widgets. """ - AppearanceModeTracker.remove(self._set_appearance_mode) - ScalingTracker.remove_widget(self._set_scaling, self) - super().destroy() - def place(self, **kwargs): """ Place a widget in the parent widget. Use as options: diff --git a/customtkinter/widgets/ctk_button.py b/customtkinter/windows/widgets/ctk_button.py similarity index 99% rename from customtkinter/widgets/ctk_button.py rename to customtkinter/windows/widgets/ctk_button.py index 172c8cc..f2c83c2 100644 --- a/customtkinter/widgets/ctk_button.py +++ b/customtkinter/windows/widgets/ctk_button.py @@ -3,7 +3,7 @@ import sys from typing import Union, Tuple, Callable from .core_rendering.ctk_canvas import CTkCanvas -from ..theme_manager import ThemeManager +from .theme.theme_manager import ThemeManager from .core_rendering.draw_engine import DrawEngine from .core_widget_classes.widget_base_class import CTkBaseClass from .font.ctk_font import CTkFont diff --git a/customtkinter/widgets/ctk_checkbox.py b/customtkinter/windows/widgets/ctk_checkbox.py similarity index 99% rename from customtkinter/widgets/ctk_checkbox.py rename to customtkinter/windows/widgets/ctk_checkbox.py index b4891a2..ed2d459 100644 --- a/customtkinter/widgets/ctk_checkbox.py +++ b/customtkinter/windows/widgets/ctk_checkbox.py @@ -3,7 +3,7 @@ import sys from typing import Union, Tuple, Callable from .core_rendering.ctk_canvas import CTkCanvas -from ..theme_manager import ThemeManager +from .theme.theme_manager import ThemeManager from .core_rendering.draw_engine import DrawEngine from .core_widget_classes.widget_base_class import CTkBaseClass from .font.ctk_font import CTkFont diff --git a/customtkinter/widgets/ctk_combobox.py b/customtkinter/windows/widgets/ctk_combobox.py similarity index 99% rename from customtkinter/widgets/ctk_combobox.py rename to customtkinter/windows/widgets/ctk_combobox.py index 63bca4e..6889664 100644 --- a/customtkinter/widgets/ctk_combobox.py +++ b/customtkinter/windows/widgets/ctk_combobox.py @@ -4,7 +4,7 @@ from typing import Union, Tuple, Callable, List from .core_widget_classes.dropdown_menu import DropdownMenu from .core_rendering.ctk_canvas import CTkCanvas -from ..theme_manager import ThemeManager +from .theme.theme_manager import ThemeManager from .core_rendering.draw_engine import DrawEngine from .core_widget_classes.widget_base_class import CTkBaseClass from .font.ctk_font import CTkFont diff --git a/customtkinter/widgets/ctk_entry.py b/customtkinter/windows/widgets/ctk_entry.py similarity index 99% rename from customtkinter/widgets/ctk_entry.py rename to customtkinter/windows/widgets/ctk_entry.py index 290196f..bb4fc3d 100644 --- a/customtkinter/widgets/ctk_entry.py +++ b/customtkinter/windows/widgets/ctk_entry.py @@ -2,7 +2,7 @@ import tkinter from typing import Union, Tuple from .core_rendering.ctk_canvas import CTkCanvas -from ..theme_manager import ThemeManager +from .theme.theme_manager import ThemeManager from .core_rendering.draw_engine import DrawEngine from .core_widget_classes.widget_base_class import CTkBaseClass from .font.ctk_font import CTkFont diff --git a/customtkinter/widgets/ctk_frame.py b/customtkinter/windows/widgets/ctk_frame.py similarity index 99% rename from customtkinter/widgets/ctk_frame.py rename to customtkinter/windows/widgets/ctk_frame.py index 1dcf946..93048ee 100644 --- a/customtkinter/widgets/ctk_frame.py +++ b/customtkinter/windows/widgets/ctk_frame.py @@ -1,7 +1,7 @@ from typing import Union, Tuple, List from .core_rendering.ctk_canvas import CTkCanvas -from ..theme_manager import ThemeManager +from .theme.theme_manager import ThemeManager from .core_rendering.draw_engine import DrawEngine from .core_widget_classes.widget_base_class import CTkBaseClass diff --git a/customtkinter/widgets/ctk_label.py b/customtkinter/windows/widgets/ctk_label.py similarity index 99% rename from customtkinter/widgets/ctk_label.py rename to customtkinter/windows/widgets/ctk_label.py index ddb150c..d0c71a1 100644 --- a/customtkinter/widgets/ctk_label.py +++ b/customtkinter/windows/widgets/ctk_label.py @@ -2,7 +2,7 @@ import tkinter from typing import Union, Tuple, Callable from .core_rendering.ctk_canvas import CTkCanvas -from ..theme_manager import ThemeManager +from .theme.theme_manager import ThemeManager from .core_rendering.draw_engine import DrawEngine from .core_widget_classes.widget_base_class import CTkBaseClass from .font.ctk_font import CTkFont diff --git a/customtkinter/widgets/ctk_optionmenu.py b/customtkinter/windows/widgets/ctk_optionmenu.py similarity index 99% rename from customtkinter/widgets/ctk_optionmenu.py rename to customtkinter/windows/widgets/ctk_optionmenu.py index 89cc8eb..00541c1 100644 --- a/customtkinter/widgets/ctk_optionmenu.py +++ b/customtkinter/windows/widgets/ctk_optionmenu.py @@ -3,7 +3,7 @@ import sys from typing import Union, Tuple, Callable from .core_rendering.ctk_canvas import CTkCanvas -from ..theme_manager import ThemeManager +from .theme.theme_manager import ThemeManager from .core_rendering.draw_engine import DrawEngine from .core_widget_classes.widget_base_class import CTkBaseClass from .core_widget_classes.dropdown_menu import DropdownMenu diff --git a/customtkinter/widgets/ctk_progressbar.py b/customtkinter/windows/widgets/ctk_progressbar.py similarity index 99% rename from customtkinter/widgets/ctk_progressbar.py rename to customtkinter/windows/widgets/ctk_progressbar.py index 68e7c72..88ab230 100644 --- a/customtkinter/widgets/ctk_progressbar.py +++ b/customtkinter/windows/widgets/ctk_progressbar.py @@ -3,7 +3,7 @@ import math from typing import Union, Tuple from .core_rendering.ctk_canvas import CTkCanvas -from ..theme_manager import ThemeManager +from .theme.theme_manager import ThemeManager from .core_rendering.draw_engine import DrawEngine from .core_widget_classes.widget_base_class import CTkBaseClass diff --git a/customtkinter/widgets/ctk_radiobutton.py b/customtkinter/windows/widgets/ctk_radiobutton.py similarity index 99% rename from customtkinter/widgets/ctk_radiobutton.py rename to customtkinter/windows/widgets/ctk_radiobutton.py index 100b3d4..4946625 100644 --- a/customtkinter/widgets/ctk_radiobutton.py +++ b/customtkinter/windows/widgets/ctk_radiobutton.py @@ -3,7 +3,7 @@ import sys from typing import Union, Tuple, Callable from .core_rendering.ctk_canvas import CTkCanvas -from ..theme_manager import ThemeManager +from .theme.theme_manager import ThemeManager from .core_rendering.draw_engine import DrawEngine from .core_widget_classes.widget_base_class import CTkBaseClass from .font.ctk_font import CTkFont diff --git a/customtkinter/widgets/ctk_scrollbar.py b/customtkinter/windows/widgets/ctk_scrollbar.py similarity index 99% rename from customtkinter/widgets/ctk_scrollbar.py rename to customtkinter/windows/widgets/ctk_scrollbar.py index bb2ff46..2e76e06 100644 --- a/customtkinter/widgets/ctk_scrollbar.py +++ b/customtkinter/windows/widgets/ctk_scrollbar.py @@ -2,7 +2,7 @@ import sys from typing import Union, Tuple, Callable from .core_rendering.ctk_canvas import CTkCanvas -from ..theme_manager import ThemeManager +from .theme.theme_manager import ThemeManager from .core_rendering.draw_engine import DrawEngine from .core_widget_classes.widget_base_class import CTkBaseClass diff --git a/customtkinter/widgets/ctk_segmented_button.py b/customtkinter/windows/widgets/ctk_segmented_button.py similarity index 99% rename from customtkinter/widgets/ctk_segmented_button.py rename to customtkinter/windows/widgets/ctk_segmented_button.py index 5565d99..a39b063 100644 --- a/customtkinter/widgets/ctk_segmented_button.py +++ b/customtkinter/windows/widgets/ctk_segmented_button.py @@ -1,7 +1,7 @@ import tkinter from typing import Union, Tuple, List, Dict, Callable -from ..theme_manager import ThemeManager +from .theme.theme_manager import ThemeManager from .ctk_button import CTkButton from .ctk_frame import CTkFrame from .font.ctk_font import CTkFont diff --git a/customtkinter/widgets/ctk_slider.py b/customtkinter/windows/widgets/ctk_slider.py similarity index 99% rename from customtkinter/widgets/ctk_slider.py rename to customtkinter/windows/widgets/ctk_slider.py index d97c739..c8011ac 100644 --- a/customtkinter/widgets/ctk_slider.py +++ b/customtkinter/windows/widgets/ctk_slider.py @@ -3,7 +3,7 @@ import sys from typing import Union, Tuple, Callable from .core_rendering.ctk_canvas import CTkCanvas -from ..theme_manager import ThemeManager +from .theme.theme_manager import ThemeManager from .core_rendering.draw_engine import DrawEngine from .core_widget_classes.widget_base_class import CTkBaseClass diff --git a/customtkinter/widgets/ctk_switch.py b/customtkinter/windows/widgets/ctk_switch.py similarity index 99% rename from customtkinter/widgets/ctk_switch.py rename to customtkinter/windows/widgets/ctk_switch.py index 22d1e52..726033d 100644 --- a/customtkinter/widgets/ctk_switch.py +++ b/customtkinter/windows/widgets/ctk_switch.py @@ -3,7 +3,7 @@ import sys from typing import Union, Tuple, Callable from .core_rendering.ctk_canvas import CTkCanvas -from ..theme_manager import ThemeManager +from .theme.theme_manager import ThemeManager from .core_rendering.draw_engine import DrawEngine from .core_widget_classes.widget_base_class import CTkBaseClass from .font.ctk_font import CTkFont diff --git a/customtkinter/widgets/ctk_tabview.py b/customtkinter/windows/widgets/ctk_tabview.py similarity index 99% rename from customtkinter/widgets/ctk_tabview.py rename to customtkinter/windows/widgets/ctk_tabview.py index b9d89cf..d3065cb 100644 --- a/customtkinter/widgets/ctk_tabview.py +++ b/customtkinter/windows/widgets/ctk_tabview.py @@ -1,6 +1,6 @@ from typing import Union, Tuple, Dict, List, Callable -from ..theme_manager import ThemeManager +from .theme.theme_manager import ThemeManager from .ctk_frame import CTkFrame from .core_widget_classes.widget_base_class import CTkBaseClass from .ctk_segmented_button import CTkSegmentedButton diff --git a/customtkinter/widgets/ctk_textbox.py b/customtkinter/windows/widgets/ctk_textbox.py similarity index 99% rename from customtkinter/widgets/ctk_textbox.py rename to customtkinter/windows/widgets/ctk_textbox.py index bfca4fd..a914c93 100644 --- a/customtkinter/widgets/ctk_textbox.py +++ b/customtkinter/windows/widgets/ctk_textbox.py @@ -3,7 +3,7 @@ from typing import Union, Tuple from .core_rendering.ctk_canvas import CTkCanvas from .ctk_scrollbar import CTkScrollbar -from ..theme_manager import ThemeManager +from .theme.theme_manager import ThemeManager from .core_rendering.draw_engine import DrawEngine from .core_widget_classes.widget_base_class import CTkBaseClass from .font.ctk_font import CTkFont diff --git a/customtkinter/widgets/image/__init__.py b/customtkinter/windows/widgets/font/__init__.py similarity index 100% rename from customtkinter/widgets/image/__init__.py rename to customtkinter/windows/widgets/font/__init__.py diff --git a/customtkinter/widgets/font/ctk_font.py b/customtkinter/windows/widgets/font/ctk_font.py similarity index 98% rename from customtkinter/widgets/font/ctk_font.py rename to customtkinter/windows/widgets/font/ctk_font.py index 83f8d69..16775f7 100644 --- a/customtkinter/widgets/font/ctk_font.py +++ b/customtkinter/windows/widgets/font/ctk_font.py @@ -2,7 +2,7 @@ from tkinter.font import Font import copy from typing import List, Callable, Tuple -from customtkinter.theme_manager import ThemeManager +from ..theme.theme_manager import ThemeManager class CTkFont(Font): diff --git a/customtkinter/widgets/font/font_manager.py b/customtkinter/windows/widgets/font/font_manager.py similarity index 100% rename from customtkinter/widgets/font/font_manager.py rename to customtkinter/windows/widgets/font/font_manager.py diff --git a/customtkinter/windows/widgets/image/__init__.py b/customtkinter/windows/widgets/image/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/customtkinter/widgets/image/ctk_image.py b/customtkinter/windows/widgets/image/ctk_image.py similarity index 100% rename from customtkinter/widgets/image/ctk_image.py rename to customtkinter/windows/widgets/image/ctk_image.py diff --git a/customtkinter/windows/widgets/scaling/__init__.py b/customtkinter/windows/widgets/scaling/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/customtkinter/windows/widgets/scaling/scaling_base_class.py b/customtkinter/windows/widgets/scaling/scaling_base_class.py new file mode 100644 index 0000000..a27a33c --- /dev/null +++ b/customtkinter/windows/widgets/scaling/scaling_base_class.py @@ -0,0 +1,133 @@ +from typing import Union, Tuple +from abc import ABC, abstractmethod +import copy +import re +try: + from typing import Literal +except ImportError: + from typing_extensions import Literal + +from .scaling_tracker import ScalingTracker +from ..font.ctk_font import CTkFont + + +class CTkScalingBaseClass(ABC): + def __init__(self, scaling_type: Literal["widget", "window"] = "widget"): + self._scaling_type = scaling_type + + if self._scaling_type == "widget": + ScalingTracker.add_widget(self._set_scaling, self) # add callback for automatic scaling changes + self._widget_scaling = ScalingTracker.get_widget_scaling(self) + elif self._scaling_type == "window": + ScalingTracker.activate_high_dpi_awareness() # make process DPI aware + ScalingTracker.add_window(self._set_scaling, self) # add callback for automatic scaling changes + self._window_scaling = ScalingTracker.get_window_scaling(self) + + def destroy(self): + if self._scaling_type == "widget": + ScalingTracker.remove_widget(self._set_scaling, self) + elif self._scaling_type == "window": + ScalingTracker.remove_window(self._set_scaling, self) + + def _apply_widget_scaling(self, value: Union[int, float, str]) -> Union[float, str]: + assert self._scaling_type == "widget" + + if isinstance(value, (int, float)): + return value * self._widget_scaling + else: + return value + + def _apply_font_scaling(self, font: Union[Tuple, CTkFont]) -> tuple: + """ Takes CTkFont object and returns tuple font with scaled size, has to be called again for every change of font object """ + assert self._scaling_type == "widget" + + if type(font) == tuple: + if len(font) == 1: + return font + elif len(font) == 2: + return font[0], -abs(round(font[1] * self._widget_scaling)) + elif len(font) == 3: + return font[0], -abs(round(font[1] * self._widget_scaling)), font[2] + else: + raise ValueError(f"Can not scale font {font}. font needs to be tuple of len 1, 2 or 3") + + elif isinstance(font, CTkFont): + return font.create_scaled_tuple(self._widget_scaling) + else: + raise ValueError(f"Can not scale font '{font}' of type {type(font)}. font needs to be tuple or instance of CTkFont") + + def _apply_argument_scaling(self, kwargs: dict) -> dict: + assert self._scaling_type == "widget" + + scaled_kwargs = copy.copy(kwargs) + + if "pady" in scaled_kwargs: + if isinstance(scaled_kwargs["pady"], (int, float, str)): + scaled_kwargs["pady"] = self._apply_widget_scaling(scaled_kwargs["pady"]) + elif isinstance(scaled_kwargs["pady"], tuple): + scaled_kwargs["pady"] = tuple([self._apply_widget_scaling(v) for v in scaled_kwargs["pady"]]) + if "padx" in kwargs: + if isinstance(scaled_kwargs["padx"], (int, float, str)): + scaled_kwargs["padx"] = self._apply_widget_scaling(scaled_kwargs["padx"]) + elif isinstance(scaled_kwargs["padx"], tuple): + scaled_kwargs["padx"] = tuple([self._apply_widget_scaling(v) for v in scaled_kwargs["padx"]]) + + if "x" in scaled_kwargs: + scaled_kwargs["x"] = self._apply_widget_scaling(scaled_kwargs["x"]) + if "y" in scaled_kwargs: + scaled_kwargs["y"] = self._apply_widget_scaling(scaled_kwargs["y"]) + + return scaled_kwargs + + @staticmethod + def _parse_geometry_string(geometry_string: str) -> tuple: + # index: 1 2 3 4 5 6 + # regex group structure: ('x', '', '', '+-+-', '-', '-') + result = re.search(r"((\d+)x(\d+)){0,1}(\+{0,1}([+-]{0,1}\d+)\+{0,1}([+-]{0,1}\d+)){0,1}", geometry_string) + + width = int(result.group(2)) if result.group(2) is not None else None + height = int(result.group(3)) if result.group(3) is not None else None + x = int(result.group(5)) if result.group(5) is not None else None + y = int(result.group(6)) if result.group(6) is not None else None + + return width, height, x, y + + def _apply_geometry_scaling(self, geometry_string: str) -> str: + assert self._scaling_type == "window" + + width, height, x, y = self._parse_geometry_string(geometry_string) + + if x is None and y is None: # no and in geometry_string + return f"{round(width * self._window_scaling)}x{round(height * self._window_scaling)}" + + elif width is None and height is None: # no and in geometry_string + return f"+{x}+{y}" + + else: + return f"{round(width * self._window_scaling)}x{round(height * self._window_scaling)}+{x}+{y}" + + def _reverse_geometry_scaling(self, scaled_geometry_string: str) -> str: + assert self._scaling_type == "window" + + width, height, x, y = self._parse_geometry_string(scaled_geometry_string) + + if x is None and y is None: # no and in geometry_string + return f"{round(width / self._window_scaling)}x{round(height / self._window_scaling)}" + + elif width is None and height is None: # no and in geometry_string + return f"+{x}+{y}" + + else: + return f"{round(width / self._window_scaling)}x{round(height / self._window_scaling)}+{x}+{y}" + + def _apply_window_scaling(self, value): + assert self._scaling_type == "window" + + if isinstance(value, (int, float)): + return int(value * self._window_scaling) + else: + return value + + @abstractmethod + def _set_scaling(self, new_widget_scaling, new_window_scaling): + return diff --git a/customtkinter/scaling_tracker.py b/customtkinter/windows/widgets/scaling/scaling_tracker.py similarity index 100% rename from customtkinter/scaling_tracker.py rename to customtkinter/windows/widgets/scaling/scaling_tracker.py diff --git a/customtkinter/windows/widgets/theme/__init__.py b/customtkinter/windows/widgets/theme/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/customtkinter/theme_manager.py b/customtkinter/windows/widgets/theme/theme_manager.py similarity index 86% rename from customtkinter/theme_manager.py rename to customtkinter/windows/widgets/theme/theme_manager.py index 5a882aa..e3387c9 100644 --- a/customtkinter/theme_manager.py +++ b/customtkinter/windows/widgets/theme/theme_manager.py @@ -13,7 +13,7 @@ class ThemeManager: script_directory = os.path.dirname(os.path.abspath(__file__)) if theme_name_or_path in cls.built_in_themes: - with open(os.path.join(script_directory, "assets", "themes", f"{theme_name_or_path}.json"), "r") as f: + with open(os.path.join(script_directory, "../../../assets", "themes", f"{theme_name_or_path}.json"), "r") as f: cls.theme = json.load(f) else: with open(theme_name_or_path, "r") as f: diff --git a/requirements.txt b/requirements.txt index e2ec11a..9c2eaa9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,2 @@ -darkdetect~=0.3.1 \ No newline at end of file +darkdetect~=0.3.1 +typing-extensions~=4.4.0