diff --git a/CHANGELOG.md b/CHANGELOG.md index f9036de..0ca4c7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ ToDo: - enforce font size in pixel and enforce CTkFont class - complete other theme files - auto scaling of images + - change font attribute in wiki ## Unreleased - 2022-10-2 ### Added @@ -16,9 +17,10 @@ ToDo: - Added CTkTabview - Added .cget() method to all widgets and windows - Added .bind() and .focus() methods to almost all widgets + - Added 'anchor' option to CTkOptionMenu and 'justify' option to CTkComboBox ### Changed - - Changed 'text_font' attribute to 'font' in all widgets + - Changed 'text_font' attribute to 'font' in all widgets, changed 'dropdown_text_font' to 'dropdown_font' - Changed 'dropdown_color' attribute to 'dropdown_fg_color' for combobox, optionmenu - Changed 'orient' attribute of CTkProgressBar and CTkSlider to 'orientation' - Width and height attributes of CTkCheckBox, CTkRadioButton, CTkSwitch now describe the outer dimensions of the whole widget. The button/switch size is described by separate attributes like checkbox_width, checkbox_height @@ -26,6 +28,7 @@ ToDo: ### Removed - Removed setter and getter functions like set_text in CTkButton - Removed bg and background attribute from CTk and CTkToplevel, always use fg_color + - Removed Settings class and moved settings to widget and window classes ### Fixed diff --git a/customtkinter/__init__.py b/customtkinter/__init__.py index 99a9307..e3fe7ee 100644 --- a/customtkinter/__init__.py +++ b/customtkinter/__init__.py @@ -7,7 +7,6 @@ from tkinter import StringVar, IntVar, DoubleVar, BooleanVar from tkinter import filedialog # import manager classes -from .settings import Settings from .appearance_mode_tracker import AppearanceModeTracker from .theme_manager import ThemeManager from .scaling_tracker import ScalingTracker diff --git a/customtkinter/settings.py b/customtkinter/settings.py deleted file mode 100644 index f75e71e..0000000 --- a/customtkinter/settings.py +++ /dev/null @@ -1,6 +0,0 @@ - -class Settings: - cursor_manipulation_enabled = True - deactivate_macos_window_header_manipulation = False - deactivate_windows_window_header_manipulation = False - # use_dropdown_fallback = True diff --git a/customtkinter/widgets/ctk_button.py b/customtkinter/widgets/ctk_button.py index 79f6341..2bef31a 100644 --- a/customtkinter/widgets/ctk_button.py +++ b/customtkinter/widgets/ctk_button.py @@ -4,7 +4,6 @@ from typing import Union, Tuple, Callable from .ctk_canvas import CTkCanvas from ..theme_manager import ThemeManager -from ..settings import Settings from ..draw_engine import DrawEngine from .widget_base_class import CTkBaseClass from ..utility.ctk_font import CTkFont @@ -334,6 +333,9 @@ class CTkButton(CTkBaseClass): self._text_color = kwargs.pop("text_color") require_redraw = True + if "hover" in kwargs: + self._hover = kwargs.pop("hover") + if "command" in kwargs: self._command = kwargs.pop("command") @@ -387,21 +389,21 @@ class CTkButton(CTkBaseClass): return super().cget(attribute_name) def _set_cursor(self): - if Settings.cursor_manipulation_enabled: + if self._cursor_manipulation_enabled: if self._state == tkinter.DISABLED: - if sys.platform == "darwin" and self._command is not None and Settings.cursor_manipulation_enabled: + if sys.platform == "darwin" and self._command is not None: self.configure(cursor="arrow") - elif sys.platform.startswith("win") and self._command is not None and Settings.cursor_manipulation_enabled: + elif sys.platform.startswith("win") and self._command is not None: self.configure(cursor="arrow") elif self._state == tkinter.NORMAL: - if sys.platform == "darwin" and self._command is not None and Settings.cursor_manipulation_enabled: + if sys.platform == "darwin" and self._command is not None: self.configure(cursor="pointinghand") - elif sys.platform.startswith("win") and self._command is not None and Settings.cursor_manipulation_enabled: + elif sys.platform.startswith("win") and self._command is not None: self.configure(cursor="hand2") def _on_enter(self, event=None): - if self._hover is True and self._state == tkinter.NORMAL: + if self._hover is True and self._state == "normal": if self._hover_color is None: inner_parts_color = self._fg_color else: @@ -423,24 +425,23 @@ class CTkButton(CTkBaseClass): def _on_leave(self, event=None): self._click_animation_running = False - if self._hover is True: - if self._fg_color is None: - inner_parts_color = self._bg_color - else: - inner_parts_color = self._fg_color + if self._fg_color is None: + inner_parts_color = self._bg_color + else: + inner_parts_color = self._fg_color - # set color of inner button parts - self._canvas.itemconfig("inner_parts", - outline=ThemeManager.single_color(inner_parts_color, self._appearance_mode), - fill=ThemeManager.single_color(inner_parts_color, self._appearance_mode)) + # set color of inner button parts + self._canvas.itemconfig("inner_parts", + outline=ThemeManager.single_color(inner_parts_color, self._appearance_mode), + fill=ThemeManager.single_color(inner_parts_color, self._appearance_mode)) - # set text_label bg color (label color) - if self._text_label is not None: - self._text_label.configure(bg=ThemeManager.single_color(inner_parts_color, self._appearance_mode)) + # set text_label bg color (label color) + if self._text_label is not None: + self._text_label.configure(bg=ThemeManager.single_color(inner_parts_color, self._appearance_mode)) - # set image_label bg color (image bg color) - if self._image_label is not None: - self._image_label.configure(bg=ThemeManager.single_color(inner_parts_color, self._appearance_mode)) + # set image_label bg color (image bg color) + if self._image_label is not None: + self._image_label.configure(bg=ThemeManager.single_color(inner_parts_color, self._appearance_mode)) def _click_animation(self): if self._click_animation_running: diff --git a/customtkinter/widgets/ctk_checkbox.py b/customtkinter/widgets/ctk_checkbox.py index af5b7b8..6c423a7 100644 --- a/customtkinter/widgets/ctk_checkbox.py +++ b/customtkinter/widgets/ctk_checkbox.py @@ -4,7 +4,6 @@ from typing import Union, Tuple, Callable from .ctk_canvas import CTkCanvas from ..theme_manager import ThemeManager -from ..settings import Settings from ..draw_engine import DrawEngine from .widget_base_class import CTkBaseClass from ..utility.ctk_font import CTkFont @@ -259,6 +258,9 @@ class CTkCheckBox(CTkBaseClass): self._border_color = kwargs.pop("border_color") require_redraw = True + if "hover" in kwargs: + self._hover = kwargs.pop("hover") + if "command" in kwargs: self._command = kwargs.pop("command") @@ -322,23 +324,23 @@ class CTkCheckBox(CTkBaseClass): return super().cget(attribute_name) def _set_cursor(self): - if Settings.cursor_manipulation_enabled: + if self._cursor_manipulation_enabled: if self._state == tkinter.DISABLED: - if sys.platform == "darwin" and Settings.cursor_manipulation_enabled: + if sys.platform == "darwin": self._canvas.configure(cursor="arrow") if self._text_label is not None: self._text_label.configure(cursor="arrow") - elif sys.platform.startswith("win") and Settings.cursor_manipulation_enabled: + elif sys.platform.startswith("win"): self._canvas.configure(cursor="arrow") if self._text_label is not None: self._text_label.configure(cursor="arrow") elif self._state == tkinter.NORMAL: - if sys.platform == "darwin" and Settings.cursor_manipulation_enabled: + if sys.platform == "darwin": self._canvas.configure(cursor="pointinghand") if self._text_label is not None: self._text_label.configure(cursor="pointinghand") - elif sys.platform.startswith("win") and Settings.cursor_manipulation_enabled: + elif sys.platform.startswith("win"): self._canvas.configure(cursor="hand2") if self._text_label is not None: self._text_label.configure(cursor="hand2") @@ -358,21 +360,20 @@ class CTkCheckBox(CTkBaseClass): outline=ThemeManager.single_color(self._hover_color, self._appearance_mode)) def _on_leave(self, event=0): - if self._hover is True: - if self._check_state is True: - self._canvas.itemconfig("inner_parts", - fill=ThemeManager.single_color(self._fg_color, self._appearance_mode), - outline=ThemeManager.single_color(self._fg_color, self._appearance_mode)) - self._canvas.itemconfig("border_parts", - fill=ThemeManager.single_color(self._fg_color, self._appearance_mode), - outline=ThemeManager.single_color(self._fg_color, self._appearance_mode)) - else: - self._canvas.itemconfig("inner_parts", - fill=ThemeManager.single_color(self._bg_color, self._appearance_mode), - outline=ThemeManager.single_color(self._bg_color, self._appearance_mode)) - self._canvas.itemconfig("border_parts", - fill=ThemeManager.single_color(self._border_color, self._appearance_mode), - outline=ThemeManager.single_color(self._border_color, self._appearance_mode)) + if self._check_state is True: + self._canvas.itemconfig("inner_parts", + fill=ThemeManager.single_color(self._fg_color, self._appearance_mode), + outline=ThemeManager.single_color(self._fg_color, self._appearance_mode)) + self._canvas.itemconfig("border_parts", + fill=ThemeManager.single_color(self._fg_color, self._appearance_mode), + outline=ThemeManager.single_color(self._fg_color, self._appearance_mode)) + else: + self._canvas.itemconfig("inner_parts", + fill=ThemeManager.single_color(self._bg_color, self._appearance_mode), + outline=ThemeManager.single_color(self._bg_color, self._appearance_mode)) + self._canvas.itemconfig("border_parts", + fill=ThemeManager.single_color(self._border_color, self._appearance_mode), + outline=ThemeManager.single_color(self._border_color, self._appearance_mode)) def _variable_callback(self, var_name, index, mode): if not self._variable_callback_blocked: diff --git a/customtkinter/widgets/ctk_combobox.py b/customtkinter/widgets/ctk_combobox.py index 7226515..865026b 100644 --- a/customtkinter/widgets/ctk_combobox.py +++ b/customtkinter/widgets/ctk_combobox.py @@ -5,7 +5,6 @@ from typing import Union, Tuple, Callable, List from .dropdown_menu import DropdownMenu from .ctk_canvas import CTkCanvas from ..theme_manager import ThemeManager -from ..settings import Settings from ..draw_engine import DrawEngine from .widget_base_class import CTkBaseClass from ..utility.ctk_font import CTkFont @@ -36,12 +35,13 @@ class CTkComboBox(CTkBaseClass): text_color_disabled: Union[str, Tuple[str, str]] = "default_theme", font: Union[tuple, CTkFont] = "default_theme", - dropdown_text_font: any = "default_theme", + dropdown_font: any = "default_theme", values: List[str] = None, state: str = tkinter.NORMAL, hover: bool = True, variable: tkinter.Variable = None, command: Callable = None, + justify: str = "left", **kwargs): # transfer basic functionality (_bg_color, size, _appearance_mode, scaling) to CTkBaseClass @@ -83,7 +83,7 @@ class CTkComboBox(CTkBaseClass): fg_color=dropdown_fg_color, hover_color=dropdown_hover_color, text_color=dropdown_text_color, - font=dropdown_text_font) + font=dropdown_font) # configure grid system (1x1) self.grid_rowconfigure(0, weight=1) @@ -99,6 +99,7 @@ class CTkComboBox(CTkBaseClass): state=self._state, width=1, bd=0, + justify=justify, highlightthickness=0, font=self._apply_font_scaling(self._font)) @@ -252,6 +253,9 @@ class CTkComboBox(CTkBaseClass): self._update_font() + if "hover" in kwargs: + self._hover = kwargs.pop("hover") + if "command" in kwargs: self._command = kwargs.pop("command") @@ -272,8 +276,11 @@ class CTkComboBox(CTkBaseClass): if "dropdown_text_color" in kwargs: self._dropdown_menu.configure(text_color=kwargs.pop("dropdown_text_color")) - if "dropdown_text_font" in kwargs: - self._dropdown_menu.configure(text_font=kwargs.pop("dropdown_text_font")) + if "dropdown_font" in kwargs: + self._dropdown_menu.configure(font=kwargs.pop("dropdown_font")) + + if "justify" in kwargs: + self._entry.configure(justify=kwargs.pop("justify")) super().configure(require_redraw=require_redraw, **kwargs) @@ -304,8 +311,8 @@ class CTkComboBox(CTkBaseClass): elif attribute_name == "font": return self._font - elif attribute_name == "dropdown_text_font": - return self._dropdown_menu.cget("text_font") + elif attribute_name == "dropdown_font": + return self._dropdown_menu.cget("font") elif attribute_name == "values": return self._values elif attribute_name == "state": @@ -316,14 +323,16 @@ class CTkComboBox(CTkBaseClass): return self._variable elif attribute_name == "command": return self._command + elif attribute_name == "justify": + return self._entry.cget("justify") else: return super().cget(attribute_name) def _on_enter(self, event=0): if self._hover is True and self._state == tkinter.NORMAL and len(self._values) > 0: - if sys.platform == "darwin" and len(self._values) > 0 and Settings.cursor_manipulation_enabled: + if sys.platform == "darwin" and len(self._values) > 0 and self._cursor_manipulation_enabled: self._canvas.configure(cursor="pointinghand") - elif sys.platform.startswith("win") and len(self._values) > 0 and Settings.cursor_manipulation_enabled: + elif sys.platform.startswith("win") and len(self._values) > 0 and self._cursor_manipulation_enabled: self._canvas.configure(cursor="hand2") # set color of inner button parts to hover color @@ -335,19 +344,18 @@ class CTkComboBox(CTkBaseClass): fill=ThemeManager.single_color(self._button_hover_color, self._appearance_mode)) def _on_leave(self, event=0): - if self._hover is True: - if sys.platform == "darwin" and len(self._values) > 0 and Settings.cursor_manipulation_enabled: - self._canvas.configure(cursor="arrow") - elif sys.platform.startswith("win") and len(self._values) > 0 and Settings.cursor_manipulation_enabled: - self._canvas.configure(cursor="arrow") + if sys.platform == "darwin" and len(self._values) > 0 and self._cursor_manipulation_enabled: + self._canvas.configure(cursor="arrow") + elif sys.platform.startswith("win") and len(self._values) > 0 and self._cursor_manipulation_enabled: + self._canvas.configure(cursor="arrow") - # set color of inner button parts - self._canvas.itemconfig("inner_parts_right", - outline=ThemeManager.single_color(self._button_color, self._appearance_mode), - fill=ThemeManager.single_color(self._button_color, self._appearance_mode)) - self._canvas.itemconfig("border_parts_right", - outline=ThemeManager.single_color(self._button_color, self._appearance_mode), - fill=ThemeManager.single_color(self._button_color, self._appearance_mode)) + # set color of inner button parts + self._canvas.itemconfig("inner_parts_right", + outline=ThemeManager.single_color(self._button_color, self._appearance_mode), + fill=ThemeManager.single_color(self._button_color, self._appearance_mode)) + self._canvas.itemconfig("border_parts_right", + outline=ThemeManager.single_color(self._button_color, self._appearance_mode), + fill=ThemeManager.single_color(self._button_color, self._appearance_mode)) def _dropdown_callback(self, value: str): if self._state == "readonly": diff --git a/customtkinter/widgets/ctk_optionmenu.py b/customtkinter/widgets/ctk_optionmenu.py index 1b0e8d8..d4470bf 100644 --- a/customtkinter/widgets/ctk_optionmenu.py +++ b/customtkinter/widgets/ctk_optionmenu.py @@ -4,7 +4,6 @@ from typing import Union, Tuple, Callable from .ctk_canvas import CTkCanvas from ..theme_manager import ThemeManager -from ..settings import Settings from ..draw_engine import DrawEngine from .widget_base_class import CTkBaseClass from .dropdown_menu import DropdownMenu @@ -33,13 +32,14 @@ class CTkOptionMenu(CTkBaseClass): dropdown_text_color: Union[str, Tuple[str, str]] = "default_theme", font: any = "default_theme", - dropdown_text_font: any = "default_theme", + dropdown_font: any = "default_theme", values: list = None, variable: tkinter.Variable = None, state: str = tkinter.NORMAL, hover: bool = True, command: Callable[[str], None] = None, dynamic_resizing: bool = True, + anchor: str = "w", **kwargs): # transfer basic functionality (_bg_color, size, _appearance_mode, scaling) to CTkBaseClass @@ -57,7 +57,6 @@ class CTkOptionMenu(CTkBaseClass): self._text_color = ThemeManager.theme["color"]["text_button"] if text_color == "default_theme" else text_color self._text_color_disabled = ThemeManager.theme["color"]["text_button_disabled"] if text_color_disabled == "default_theme" else text_color_disabled self._font = (ThemeManager.theme["text"]["font"], ThemeManager.theme["text"]["size"]) if font == "default_theme" else font - self._dropdown_text_font = dropdown_text_font # callback and hover functionality self._command = command @@ -84,7 +83,7 @@ class CTkOptionMenu(CTkBaseClass): fg_color=dropdown_fg_color, hover_color=dropdown_hover_color, text_color=dropdown_text_color, - font=dropdown_text_font) + font=dropdown_font) # configure grid system (1x1) self.grid_rowconfigure(0, weight=1) @@ -94,25 +93,21 @@ class CTkOptionMenu(CTkBaseClass): highlightthickness=0, width=self._apply_widget_scaling(self._desired_width), height=self._apply_widget_scaling(self._desired_height)) - self._canvas.grid(row=0, column=0, rowspan=1, columnspan=1, sticky="nsew") self._draw_engine = DrawEngine(self._canvas) - left_section_width = self._current_width - self._current_height self._text_label = tkinter.Label(master=self, font=self._apply_font_scaling(self._font), - anchor="w", + anchor=anchor, padx=0, pady=0, borderwidth=1, text=self._current_value) - self._text_label.grid(row=0, column=0, sticky="w", - padx=(max(self._apply_widget_scaling(self._corner_radius), self._apply_widget_scaling(3)), - max(self._apply_widget_scaling(self._current_width - left_section_width + 3), self._apply_widget_scaling(3)))) + self._create_grid() if not self._dynamic_resizing: self.grid_propagate(0) - if Settings.cursor_manipulation_enabled: + if self._cursor_manipulation_enabled: if sys.platform == "darwin": self.configure(cursor="pointinghand") elif sys.platform.startswith("win"): @@ -136,18 +131,22 @@ class CTkOptionMenu(CTkBaseClass): self._current_value = self._variable.get() self._text_label.configure(text=self._current_value) - def _set_scaling(self, *args, **kwargs): - super()._set_scaling(*args, **kwargs) + def _create_grid(self): + self._canvas.grid(row=0, column=0, rowspan=1, columnspan=1, sticky="nsew") - # change label text size and grid padding left_section_width = self._current_width - self._current_height - self._text_label.configure(font=self._apply_font_scaling(self._font)) - self._text_label.grid(row=0, column=0, sticky="w", + self._text_label.grid(row=0, column=0, sticky="ew", padx=(max(self._apply_widget_scaling(self._corner_radius), self._apply_widget_scaling(3)), max(self._apply_widget_scaling(self._current_width - left_section_width + 3), self._apply_widget_scaling(3)))) + def _set_scaling(self, *args, **kwargs): + super()._set_scaling(*args, **kwargs) + + # change label font size and grid padding + 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._create_grid() self._draw(no_color_updates=True) def _set_dimensions(self, width: int = None, height: int = None): @@ -202,8 +201,9 @@ class CTkOptionMenu(CTkBaseClass): self._canvas.update_idletasks() def configure(self, require_redraw=False, **kwargs): - if "state" in kwargs: - self._state = kwargs.pop("state") + if "corner_radius" in kwargs: + self._corner_radius = kwargs.pop("corner_radius") + self._create_grid() require_redraw = True if "fg_color" in kwargs: @@ -222,6 +222,15 @@ class CTkOptionMenu(CTkBaseClass): self._text_color = kwargs.pop("text_color") require_redraw = True + if "dropdown_color" in kwargs: + self._dropdown_menu.configure(fg_color=kwargs.pop("dropdown_color")) + + if "dropdown_hover_color" in kwargs: + self._dropdown_menu.configure(hover_color=kwargs.pop("dropdown_hover_color")) + + if "dropdown_text_color" in kwargs: + self._dropdown_menu.configure(text_color=kwargs.pop("dropdown_text_color")) + if "font" in kwargs: self._font = kwargs.pop("font") self._text_label.configure(font=self._apply_font_scaling(self._font)) @@ -246,18 +255,15 @@ class CTkOptionMenu(CTkBaseClass): self._values = kwargs.pop("values") self._dropdown_menu.configure(values=self._values) - if "dropdown_color" in kwargs: - self._dropdown_menu.configure(fg_color=kwargs.pop("dropdown_color")) + if "dropdown_font" in kwargs: + self._dropdown_menu.configure(font=kwargs.pop("dropdown_font")) - if "dropdown_hover_color" in kwargs: - self._dropdown_menu.configure(hover_color=kwargs.pop("dropdown_hover_color")) + if "hover" in kwargs: + self._hover = kwargs.pop("hover") - if "dropdown_text_color" in kwargs: - self._dropdown_menu.configure(text_color=kwargs.pop("dropdown_text_color")) - - if "dropdown_text_font" in kwargs: - self._dropdown_text_font = kwargs.pop("dropdown_text_font") - self._dropdown_menu.configure(text_font=self._dropdown_text_font) + if "state" in kwargs: + self._state = kwargs.pop("state") + require_redraw = True if "dynamic_resizing" in kwargs: self._dynamic_resizing = kwargs.pop("dynamic_resizing") @@ -266,6 +272,9 @@ class CTkOptionMenu(CTkBaseClass): else: self.grid_propagate(1) + if "anchor" in kwargs: + self._text_label.configure(anchor=kwargs.pop("anchor")) + super().configure(require_redraw=require_redraw, **kwargs) def cget(self, attribute_name: str) -> any: @@ -291,8 +300,8 @@ class CTkOptionMenu(CTkBaseClass): elif attribute_name == "font": return self._font - elif attribute_name == "dropdown_text_font": - return self._dropdown_menu.cget("text_font") + elif attribute_name == "dropdown_font": + return self._dropdown_menu.cget("font") elif attribute_name == "values": return self._values elif attribute_name == "variable": @@ -305,6 +314,8 @@ class CTkOptionMenu(CTkBaseClass): return self._command elif attribute_name == "dynamic_resizing": return self._dynamic_resizing + elif attribute_name == "anchor": + return self._text_label.cget("anchor") else: return super().cget(attribute_name) @@ -321,11 +332,10 @@ class CTkOptionMenu(CTkBaseClass): fill=ThemeManager.single_color(self._button_hover_color, self._appearance_mode)) def _on_leave(self, event=0): - if self._hover is True: - # set color of inner button parts - self._canvas.itemconfig("inner_parts_right", - outline=ThemeManager.single_color(self._button_color, self._appearance_mode), - fill=ThemeManager.single_color(self._button_color, self._appearance_mode)) + # set color of inner button parts + self._canvas.itemconfig("inner_parts_right", + outline=ThemeManager.single_color(self._button_color, self._appearance_mode), + fill=ThemeManager.single_color(self._button_color, self._appearance_mode)) def _variable_callback(self, var_name, index, mode): if not self._variable_callback_blocked: diff --git a/customtkinter/widgets/ctk_radiobutton.py b/customtkinter/widgets/ctk_radiobutton.py index a67ddb3..2064acb 100644 --- a/customtkinter/widgets/ctk_radiobutton.py +++ b/customtkinter/widgets/ctk_radiobutton.py @@ -4,7 +4,6 @@ from typing import Union, Tuple, Callable from .ctk_canvas import CTkCanvas from ..theme_manager import ThemeManager -from ..settings import Settings from ..draw_engine import DrawEngine from .widget_base_class import CTkBaseClass @@ -220,6 +219,9 @@ class CTkRadioButton(CTkBaseClass): self._border_width = kwargs.pop("border_width") require_redraw = True + if "hover" in kwargs: + self._hover = kwargs.pop("hover") + if "command" in kwargs: self._command = kwargs.pop("command") @@ -284,23 +286,23 @@ class CTkRadioButton(CTkBaseClass): return super().cget(attribute_name) def _set_cursor(self): - if Settings.cursor_manipulation_enabled: + if self._cursor_manipulation_enabled: if self._state == tkinter.DISABLED: - if sys.platform == "darwin" and Settings.cursor_manipulation_enabled: + if sys.platform == "darwin": self._canvas.configure(cursor="arrow") if self._text_label is not None: self._text_label.configure(cursor="arrow") - elif sys.platform.startswith("win") and Settings.cursor_manipulation_enabled: + elif sys.platform.startswith("win"): self._canvas.configure(cursor="arrow") if self._text_label is not None: self._text_label.configure(cursor="arrow") elif self._state == tkinter.NORMAL: - if sys.platform == "darwin" and Settings.cursor_manipulation_enabled: + if sys.platform == "darwin": self._canvas.configure(cursor="pointinghand") if self._text_label is not None: self._text_label.configure(cursor="pointinghand") - elif sys.platform.startswith("win") and Settings.cursor_manipulation_enabled: + elif sys.platform.startswith("win"): self._canvas.configure(cursor="hand2") if self._text_label is not None: self._text_label.configure(cursor="hand2") @@ -312,15 +314,14 @@ class CTkRadioButton(CTkBaseClass): outline=ThemeManager.single_color(self._hover_color, self._appearance_mode)) def _on_leave(self, event=0): - if self._hover is True: - if self._check_state is True: - self._canvas.itemconfig("border_parts", - fill=ThemeManager.single_color(self._fg_color, self._appearance_mode), - outline=ThemeManager.single_color(self._fg_color, self._appearance_mode)) - else: - self._canvas.itemconfig("border_parts", - fill=ThemeManager.single_color(self._border_color, self._appearance_mode), - outline=ThemeManager.single_color(self._border_color, self._appearance_mode)) + if self._check_state is True: + self._canvas.itemconfig("border_parts", + fill=ThemeManager.single_color(self._fg_color, self._appearance_mode), + outline=ThemeManager.single_color(self._fg_color, self._appearance_mode)) + else: + self._canvas.itemconfig("border_parts", + fill=ThemeManager.single_color(self._border_color, self._appearance_mode), + outline=ThemeManager.single_color(self._border_color, self._appearance_mode)) def _variable_callback(self, var_name, index, mode): if not self._variable_callback_blocked: diff --git a/customtkinter/widgets/ctk_scrollbar.py b/customtkinter/widgets/ctk_scrollbar.py index 554c90e..e96cf0d 100644 --- a/customtkinter/widgets/ctk_scrollbar.py +++ b/customtkinter/widgets/ctk_scrollbar.py @@ -163,6 +163,9 @@ class CTkScrollbar(CTkBaseClass): self._scrollbar_hover_color = kwargs.pop("scrollbar_hover_color") require_redraw = True + if "hover" in kwargs: + self._hover = kwargs.pop("hover") + if "command" in kwargs: self._command = kwargs.pop("command") diff --git a/customtkinter/widgets/ctk_slider.py b/customtkinter/widgets/ctk_slider.py index 201735c..aed5a99 100644 --- a/customtkinter/widgets/ctk_slider.py +++ b/customtkinter/widgets/ctk_slider.py @@ -4,7 +4,6 @@ from typing import Union, Tuple, Callable from .ctk_canvas import CTkCanvas from ..theme_manager import ThemeManager -from ..settings import Settings from ..draw_engine import DrawEngine from .widget_base_class import CTkBaseClass @@ -35,6 +34,7 @@ class CTkSlider(CTkBaseClass): to: int = 1, state: str = "normal", number_of_steps: Union[int, None] = None, + hover: bool = True, command: Callable[[float], None] = None, variable: tkinter.Variable = None, orientation: str = "horizontal", @@ -70,6 +70,7 @@ class CTkSlider(CTkBaseClass): self._value: float = 0.5 # initial value of slider in percent self._orientation = orientation self._hover_state: bool = False + self._hover = hover self._from_ = from_ self._to = to self._number_of_steps = number_of_steps @@ -131,13 +132,13 @@ class CTkSlider(CTkBaseClass): super().destroy() def _set_cursor(self): - if self._state == "normal" and Settings.cursor_manipulation_enabled: + if self._state == "normal" and self._cursor_manipulation_enabled: if sys.platform == "darwin": self.configure(cursor="pointinghand") elif sys.platform.startswith("win"): self.configure(cursor="hand2") - elif self._state == "disabled" and Settings.cursor_manipulation_enabled: + elif self._state == "disabled" and self._cursor_manipulation_enabled: if sys.platform == "darwin": self.configure(cursor="arrow") elif sys.platform.startswith("win"): @@ -227,6 +228,9 @@ class CTkSlider(CTkBaseClass): if "number_of_steps" in kwargs: self._number_of_steps = kwargs.pop("number_of_steps") + if "hover" in kwargs: + self._hover = kwargs.pop("hover") + if "command" in kwargs: self._command = kwargs.pop("command") @@ -273,6 +277,8 @@ class CTkSlider(CTkBaseClass): return self._state elif attribute_name == "number_of_steps": return self._number_of_steps + elif attribute_name == "hover": + return self._hover elif attribute_name == "command": return self._command elif attribute_name == "variable": @@ -309,7 +315,7 @@ class CTkSlider(CTkBaseClass): self._command(self._output_value) def _on_enter(self, event=0): - if self._state == "normal": + if self._hover is True and self._state == "normal": self._hover_state = True self._canvas.itemconfig("slider_parts", fill=ThemeManager.single_color(self._button_hover_color, self._appearance_mode), diff --git a/customtkinter/widgets/ctk_switch.py b/customtkinter/widgets/ctk_switch.py index 9c4c31c..99e34ab 100644 --- a/customtkinter/widgets/ctk_switch.py +++ b/customtkinter/widgets/ctk_switch.py @@ -4,7 +4,6 @@ from typing import Union, Tuple, Callable from .ctk_canvas import CTkCanvas from ..theme_manager import ThemeManager -from ..settings import Settings from ..draw_engine import DrawEngine from .widget_base_class import CTkBaseClass @@ -40,6 +39,7 @@ class CTkSwitch(CTkBaseClass): onvalue: Union[int, str] = 1, offvalue: Union[int, str] = 0, variable: tkinter.Variable = None, + hover: bool = True, command: Callable = None, state: str = tkinter.NORMAL, **kwargs): @@ -72,6 +72,7 @@ class CTkSwitch(CTkBaseClass): self._button_length = ThemeManager.theme["shape"]["switch_button_length"] if button_length == "default_theme" else button_length self._hover_state: bool = False self._check_state: bool = False # True if switch is activated + self._hover = hover self._state = state self._onvalue = onvalue self._offvalue = offvalue @@ -153,23 +154,23 @@ class CTkSwitch(CTkBaseClass): super().destroy() def _set_cursor(self): - if Settings.cursor_manipulation_enabled: + if self._cursor_manipulation_enabled: if self._state == tkinter.DISABLED: - if sys.platform == "darwin" and Settings.cursor_manipulation_enabled: + if sys.platform == "darwin": self._canvas.configure(cursor="arrow") if self._text_label is not None: self._text_label.configure(cursor="arrow") - elif sys.platform.startswith("win") and Settings.cursor_manipulation_enabled: + elif sys.platform.startswith("win"): self._canvas.configure(cursor="arrow") if self._text_label is not None: self._text_label.configure(cursor="arrow") elif self._state == tkinter.NORMAL: - if sys.platform == "darwin" and Settings.cursor_manipulation_enabled: + if sys.platform == "darwin": self._canvas.configure(cursor="pointinghand") if self._text_label is not None: self._text_label.configure(cursor="pointinghand") - elif sys.platform.startswith("win") and Settings.cursor_manipulation_enabled: + elif sys.platform.startswith("win"): self._canvas.configure(cursor="hand2") if self._text_label is not None: self._text_label.configure(cursor="hand2") @@ -276,6 +277,9 @@ class CTkSwitch(CTkBaseClass): self._border_width = kwargs.pop("border_width") require_redraw = True + if "hover" in kwargs: + self._hover = kwargs.pop("hover") + if "command" in kwargs: self._command = kwargs.pop("command") @@ -335,6 +339,8 @@ class CTkSwitch(CTkBaseClass): return self._offvalue elif attribute_name == "variable": return self._variable + elif attribute_name == "hover": + return self._hover elif attribute_name == "command": return self._command elif attribute_name == "state": @@ -386,9 +392,8 @@ class CTkSwitch(CTkBaseClass): return self._onvalue if self._check_state is True else self._offvalue def _on_enter(self, event=0): - self._hover_state = True - - if self._state is not tkinter.DISABLED: + if self._hover is True and self._state == "normal": + self._hover_state = True self._canvas.itemconfig("slider_parts", fill=ThemeManager.single_color(self._button_hover_color, self._appearance_mode), outline=ThemeManager.single_color(self._button_hover_color, self._appearance_mode)) diff --git a/customtkinter/widgets/widget_base_class.py b/customtkinter/widgets/widget_base_class.py index d6a74c9..b0f06e8 100644 --- a/customtkinter/widgets/widget_base_class.py +++ b/customtkinter/widgets/widget_base_class.py @@ -25,7 +25,9 @@ class CTkBaseClass(tkinter.Frame): appearance_mode changes, scaling, bg changes of master if master is not a CTk widget """ # attributes that are passed to and managed by the tkinter frame only: - _valid_tk_frame_attributes = {"cursor"} + _valid_tk_frame_attributes: set = {"cursor"} + + _cursor_manipulation_enabled: bool = True def __init__(self, master: any = None, diff --git a/customtkinter/windows/ctk_tk.py b/customtkinter/windows/ctk_tk.py index 7e8791c..d155659 100644 --- a/customtkinter/windows/ctk_tk.py +++ b/customtkinter/windows/ctk_tk.py @@ -10,7 +10,6 @@ from typing import Union, Tuple from ..appearance_mode_tracker import AppearanceModeTracker from ..theme_manager import ThemeManager from ..scaling_tracker import ScalingTracker -from ..settings import Settings from ..utility.utility_functions import pop_from_dict_by_set, check_kwargs_empty @@ -27,6 +26,9 @@ class CTk(tkinter.Tk): 'use', 'container', 'cursor', 'height', 'highlightthickness', 'padx', 'pady', 'takefocus', 'visual', 'width'} + _deactivate_macos_window_header_manipulation = False + _deactivate_windows_window_header_manipulation = False + def __init__(self, fg_color: Union[str, Tuple[str, str]] = "default_theme", **kwargs): @@ -262,17 +264,17 @@ class CTk(tkinter.Tk): else: return super().cget(attribute_name) - @staticmethod - def _enable_macos_dark_title_bar(): - if sys.platform == "darwin" and not Settings.deactivate_macos_window_header_manipulation: # macOS + @classmethod + def _enable_macos_dark_title_bar(cls): + if sys.platform == "darwin" and not cls._deactivate_macos_window_header_manipulation: # macOS if Version(platform.python_version()) < Version("3.10"): if Version(tkinter.Tcl().call("info", "patchlevel")) >= Version("8.6.9"): # Tcl/Tk >= 8.6.9 os.system("defaults write -g NSRequiresAquaSystemAppearance -bool No") # This command allows dark-mode for all programs - @staticmethod - def _disable_macos_dark_title_bar(): - if sys.platform == "darwin" and not Settings.deactivate_macos_window_header_manipulation: # macOS + @classmethod + def _disable_macos_dark_title_bar(cls): + if sys.platform == "darwin" and not cls._deactivate_macos_window_header_manipulation: # macOS if Version(platform.python_version()) < Version("3.10"): if Version(tkinter.Tcl().call("info", "patchlevel")) >= Version("8.6.9"): # Tcl/Tk >= 8.6.9 os.system("defaults delete -g NSRequiresAquaSystemAppearance") @@ -289,7 +291,7 @@ class CTk(tkinter.Tk): https://docs.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute """ - if sys.platform.startswith("win") and not Settings.deactivate_windows_window_header_manipulation: + if sys.platform.startswith("win") and not self._deactivate_windows_window_header_manipulation: if self._window_exists: self._state_before_windows_set_titlebar_color = self.state() diff --git a/customtkinter/windows/ctk_toplevel.py b/customtkinter/windows/ctk_toplevel.py index e18270f..440cf66 100644 --- a/customtkinter/windows/ctk_toplevel.py +++ b/customtkinter/windows/ctk_toplevel.py @@ -9,7 +9,6 @@ from typing import Union, Tuple from ..appearance_mode_tracker import AppearanceModeTracker from ..theme_manager import ThemeManager -from ..settings import Settings from ..scaling_tracker import ScalingTracker from ..utility.utility_functions import pop_from_dict_by_set, check_kwargs_empty @@ -25,6 +24,9 @@ class CTkToplevel(tkinter.Toplevel): "highlightbackground", "highlightthickness", "menu", "relief", "screen", "takefocus", "use", "visual", "width"} + _deactivate_macos_window_header_manipulation = False + _deactivate_windows_window_header_manipulation = False + def __init__(self, *args, fg_color: Union[str, Tuple[str, str]] = "default_theme", **kwargs): @@ -219,16 +221,16 @@ class CTkToplevel(tkinter.Toplevel): else: return super().cget(attribute_name) - @staticmethod - def _enable_macos_dark_title_bar(): - if sys.platform == "darwin" and not Settings.deactivate_macos_window_header_manipulation: # macOS + @classmethod + def _enable_macos_dark_title_bar(cls): + if sys.platform == "darwin" and not cls._deactivate_macos_window_header_manipulation: # macOS if Version(platform.python_version()) < Version("3.10"): if Version(tkinter.Tcl().call("info", "patchlevel")) >= Version("8.6.9"): # Tcl/Tk >= 8.6.9 os.system("defaults write -g NSRequiresAquaSystemAppearance -bool No") - @staticmethod - def _disable_macos_dark_title_bar(): - if sys.platform == "darwin" and not Settings.deactivate_macos_window_header_manipulation: # macOS + @classmethod + def _disable_macos_dark_title_bar(cls): + if sys.platform == "darwin" and not cls._deactivate_macos_window_header_manipulation: # macOS if Version(platform.python_version()) < Version("3.10"): if Version(tkinter.Tcl().call("info", "patchlevel")) >= Version("8.6.9"): # Tcl/Tk >= 8.6.9 os.system("defaults delete -g NSRequiresAquaSystemAppearance") @@ -245,7 +247,7 @@ class CTkToplevel(tkinter.Toplevel): https://docs.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute """ - if sys.platform.startswith("win") and not Settings.deactivate_windows_window_header_manipulation: + if sys.platform.startswith("win") and not self._deactivate_windows_window_header_manipulation: self._state_before_windows_set_titlebar_color = self.state() super().withdraw() # hide window so that it can be redrawn after the titlebar change so that the color change is visible diff --git a/test/manual_integration_tests/complex_example_new.py b/test/manual_integration_tests/complex_example_new.py index a02d099..7da1120 100644 --- a/test/manual_integration_tests/complex_example_new.py +++ b/test/manual_integration_tests/complex_example_new.py @@ -56,20 +56,6 @@ class App(customtkinter.CTk): self.textbox = customtkinter.CTkTextbox(self) self.textbox.grid(row=0, column=1, padx=(20, 10), pady=(20, 10), sticky="nsew") - # create optionemnu and combobox frame - self.optionemnu_combobox_frame = customtkinter.CTkFrame(self) - self.optionemnu_combobox_frame.grid(row=0, column=2, padx=(10, 10), pady=(20, 10), sticky="nsew") - self.optionmenu_1 = customtkinter.CTkOptionMenu(self.optionemnu_combobox_frame, - dynamic_resizing=False, - values=["Value 1", "Value 2", "Value Long Long Long"]) - self.optionmenu_1.grid(row=0, column=0, padx=20, pady=(20, 10), sticky="ew") - self.combobox_1 = customtkinter.CTkComboBox(self.optionemnu_combobox_frame, - values=["Value 1", "Value 2", "Value Long....."]) - self.combobox_1.grid(row=1, column=0, padx=20, pady=(10, 10), sticky="ew") - self.string_input_button = customtkinter.CTkButton(self.optionemnu_combobox_frame, text="Open CTkInputDialog", - command=self.open_input_dialog) - self.string_input_button.grid(row=2, column=0, padx=20, pady=(10, 10), sticky="ew") - # create radiobutton frame self.radiobutton_frame = customtkinter.CTkFrame(self) self.radiobutton_frame.grid(row=0, column=3, padx=(10, 10), pady=(20, 10), sticky="nsew") @@ -118,6 +104,22 @@ class App(customtkinter.CTk): # create tabview self.tabview = customtkinter._CTkTabview(self) self.tabview.grid(row=1, column=3, columnspan=2, padx=(10, 20), pady=(10, 10), sticky="nsew") + self.tabview.add("CTkTabview") + self.tabview.add("Tab 2") + self.tabview.add("Tab 3") + + self.tabview.tab("CTkTabview").grid_columnconfigure(0, weight=1) + self.optionmenu_1 = customtkinter.CTkOptionMenu(self.tabview.tab("CTkTabview"), + dynamic_resizing=False, + values=["Value 1", "Value 2", "Value Long Long Long"]) + self.optionmenu_1.grid(row=0, column=0, padx=20, pady=(20, 10)) + self.combobox_1 = customtkinter.CTkComboBox(self.tabview.tab("CTkTabview"), + values=["Value 1", "Value 2", "Value Long....."]) + self.combobox_1.grid(row=1, column=0, padx=20, pady=(10, 10)) + self.string_input_button = customtkinter.CTkButton(self.tabview.tab("CTkTabview"), + text="Open CTkInputDialog", + command=self.open_input_dialog) + self.string_input_button.grid(row=2, column=0, padx=20, pady=(10, 10)) # set default values self.sidebar_button_3.configure(state="disabled", text="Disabled CTkButton")