From bfc42c25ef0f8c80eb756e6a0c3a50b62eba4079 Mon Sep 17 00:00:00 2001 From: Tom Schimansky Date: Mon, 3 Oct 2022 00:33:06 +0200 Subject: [PATCH] Added .cget() methods to all widgets and changed text_font to font for all widgets --- CHANGELOG.md | 15 +++ customtkinter/widgets/ctk_button.py | 84 ++++++++-------- customtkinter/widgets/ctk_checkbox.py | 68 ++++++++++--- customtkinter/widgets/ctk_combobox.py | 98 ++++++++++++++----- customtkinter/widgets/ctk_entry.py | 88 ++++++++++++----- customtkinter/widgets/ctk_frame.py | 23 ++++- customtkinter/widgets/ctk_label.py | 43 ++++++-- customtkinter/widgets/ctk_optionmenu.py | 78 +++++++++++---- customtkinter/widgets/ctk_progressbar.py | 50 +++++++--- customtkinter/widgets/ctk_radiobutton.py | 74 ++++++++++---- customtkinter/widgets/ctk_scrollbar.py | 40 ++++++-- customtkinter/widgets/ctk_slider.py | 68 ++++++++++--- customtkinter/widgets/ctk_switch.py | 80 +++++++++++---- customtkinter/widgets/ctk_textbox.py | 44 ++++++--- customtkinter/widgets/dropdown_menu.py | 54 +++++++--- customtkinter/widgets/widget_base_class.py | 24 +++-- .../widgets/widget_helper_functions.py | 11 +++ customtkinter/windows/ctk_input_dialog.py | 9 +- customtkinter/windows/ctk_tk.py | 6 ++ customtkinter/windows/ctk_toplevel.py | 6 ++ examples/complex_example.py | 2 +- .../complex_example_new.py | 6 +- 22 files changed, 725 insertions(+), 246 deletions(-) create mode 100644 customtkinter/widgets/widget_helper_functions.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 95e0d0e..d88e989 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,21 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased - 2022-10-2 +### Added + - added .cget() method to all widgets and windows + +### Changed + - changed 'text_font' attribute to 'font' in all widgets + - changed 'dropdown_color' attribute to 'dropdown_fg_color' for combobox, optionmenu + - changed 'orient' attribute of CTkProgressBar and CTkSlider to 'orientation' + +### Removed + - removed setter and getter functions like set_text in CTkButton + +### Fixed + + ## [4.6.0] - 2022-09-17 ### Added - CTkProgressBar indeterminate mode, automatic progress loop with .start() and .stop() diff --git a/customtkinter/widgets/ctk_button.py b/customtkinter/widgets/ctk_button.py index d1dfd24..35a99e8 100644 --- a/customtkinter/widgets/ctk_button.py +++ b/customtkinter/widgets/ctk_button.py @@ -16,24 +16,26 @@ class CTkButton(CTkBaseClass): """ def __init__(self, *args, + width: int = 140, + height: int = 28, + corner_radius: Union[int, str] = "default_theme", + border_width: Union[int, str] = "default_theme", + bg_color: Union[str, Tuple[str, str], None] = None, fg_color: Union[str, Tuple[str, str], None] = "default_theme", hover_color: Union[str, Tuple[str, str]] = "default_theme", border_color: Union[str, Tuple[str, str]] = "default_theme", text_color: Union[str, Tuple[str, str]] = "default_theme", text_color_disabled: Union[str, Tuple[str, str]] = "default_theme", - width: int = 140, - height: int = 28, - corner_radius: Union[int, str] = "default_theme", - border_width: Union[int, str] = "default_theme", + text: str = "CTkButton", + font: any = "default_theme", textvariable: tkinter.Variable = None, - text_font: any = "default_theme", image: tkinter.PhotoImage = None, - hover: bool = True, - compound: str = "left", state: str = "normal", + hover: bool = True, command: Callable = None, + compound: str = "left", **kwargs): # transfer basic functionality (_bg_color, size, _appearance_mode, scaling) to CTkBaseClass @@ -55,7 +57,7 @@ class CTkButton(CTkBaseClass): self._image_label = None self._text = text self._text_label = None - self._text_font = (ThemeManager.theme["text"]["font"], ThemeManager.theme["text"]["size"]) if text_font == "default_theme" else text_font + self._font = (ThemeManager.theme["text"]["font"], ThemeManager.theme["text"]["size"]) if font == "default_theme" else font # callback and hover functionality self._command = command @@ -141,7 +143,7 @@ class CTkButton(CTkBaseClass): if self._text_label is None: self._text_label = tkinter.Label(master=self, - font=self._apply_font_scaling(self._text_font), + font=self._apply_font_scaling(self._font), text=self._text, textvariable=self._textvariable) @@ -249,10 +251,10 @@ class CTkButton(CTkBaseClass): else: self._text_label.configure(text=self._text) - if "text_font" in kwargs: - self._text_font = kwargs.pop("text_font") + if "font" in kwargs: + self._font = kwargs.pop("font") if self._text_label is not None: - self._text_label.configure(font=self._apply_font_scaling(self._text_font)) + self._text_label.configure(font=self._apply_font_scaling(self._font)) if "state" in kwargs: self._state = kwargs.pop("state") @@ -303,37 +305,41 @@ class CTkButton(CTkBaseClass): super().configure(require_redraw=require_redraw, **kwargs) - def cget(self, key: str) -> any: - if key == "fg_color": - return self._fg_color - elif key == "hover_color": - return self._hover_color - elif key == "border_color": - return self._border_color - elif key == "text_color": - return self._text_color - elif key == "text_color_disabled": - return self._text_color_disabled - elif key == "text": - return self._text - elif key == "textvariable": - return self._textvariable - elif key == "text_font": - return self._text_font - elif key == "command": - return self._command - elif key == "hover": - return self._hover - elif key == "border_width": + def cget(self, attribute_name: str) -> any: + if attribute_name == "corner_radius": + return self._corner_radius + elif attribute_name == "border_width": return self._border_width - elif key == "image": + + elif attribute_name == "fg_color": + return self._fg_color + elif attribute_name == "hover_color": + return self._hover_color + elif attribute_name == "border_color": + return self._border_color + elif attribute_name == "text_color": + return self._text_color + elif attribute_name == "text_color_disabled": + return self._text_color_disabled + + elif attribute_name == "text": + return self._text + elif attribute_name == "font": + return self._font + elif attribute_name == "textvariable": + return self._textvariable + elif attribute_name == "image": return self._image - elif key == "compound": - return self._compound - elif key == "state": + elif attribute_name == "state": return self._state + elif attribute_name == "hover": + return self._hover + elif attribute_name == "command": + return self._command + elif attribute_name == "compound": + return self._compound else: - return super().cget(key) + return super().cget(attribute_name) def _set_cursor(self): if Settings.cursor_manipulation_enabled: diff --git a/customtkinter/widgets/ctk_checkbox.py b/customtkinter/widgets/ctk_checkbox.py index b942771..11eb27b 100644 --- a/customtkinter/widgets/ctk_checkbox.py +++ b/customtkinter/widgets/ctk_checkbox.py @@ -16,26 +16,28 @@ class CTkCheckBox(CTkBaseClass): """ def __init__(self, *args, + width: int = 24, + height: int = 24, + corner_radius: Union[int, str] = "default_theme", + border_width: Union[int, str] = "default_theme", + bg_color: Union[str, Tuple[str, str], None] = None, fg_color: Union[str, Tuple[str, str]] = "default_theme", hover_color: Union[str, Tuple[str, str]] = "default_theme", border_color: Union[str, Tuple[str, str]] = "default_theme", - border_width: Union[int, str] = "default_theme", checkmark_color: Union[str, Tuple[str, str]] = "default_theme", - width: int = 24, - height: int = 24, - corner_radius: Union[int, str] = "default_theme", - text_font: any = "default_theme", text_color: Union[str, Tuple[str, str]] = "default_theme", - text: str = "CTkCheckBox", text_color_disabled: Union[str, Tuple[str, str]] = "default_theme", + + text: str = "CTkCheckBox", + font: any = "default_theme", + textvariable: tkinter.Variable = None, + state: str = tkinter.NORMAL, hover: bool = True, command: Callable = None, - state: str = tkinter.NORMAL, onvalue: Union[int, str] = 1, offvalue: Union[int, str] = 0, variable: tkinter.Variable = None, - textvariable: tkinter.Variable = None, **kwargs): # transfer basic functionality (_bg_color, size, _appearance_mode, scaling) to CTkBaseClass @@ -56,7 +58,7 @@ class CTkCheckBox(CTkBaseClass): self._text_label: Union[tkinter.Label, None] = None self._text_color = ThemeManager.theme["color"]["text"] if text_color == "default_theme" else text_color self._text_color_disabled = ThemeManager.theme["color"]["text_disabled"] if text_color_disabled == "default_theme" else text_color_disabled - self._text_font = (ThemeManager.theme["text"]["font"], ThemeManager.theme["text"]["size"]) if text_font == "default_theme" else text_font + self._font = (ThemeManager.theme["text"]["font"], ThemeManager.theme["text"]["size"]) if font == "default_theme" else font # callback and hover functionality self._command = command @@ -98,7 +100,7 @@ class CTkCheckBox(CTkBaseClass): bd=0, text=self._text, justify=tkinter.LEFT, - font=self._apply_font_scaling(self._text_font), + font=self._apply_font_scaling(self._font), textvariable=self._textvariable) self._text_label.grid(row=0, column=2, padx=0, pady=0, sticky="w") self._text_label["anchor"] = "w" @@ -119,7 +121,7 @@ class CTkCheckBox(CTkBaseClass): super()._set_scaling(*args, **kwargs) self.grid_columnconfigure(1, weight=0, minsize=self._apply_widget_scaling(6)) - self._text_label.configure(font=self._apply_font_scaling(self._text_font)) + self._text_label.configure(font=self._apply_font_scaling(self._font)) self._canvas.delete("checkmark") self._bg_canvas.configure(width=self._apply_widget_scaling(self._desired_width), height=self._apply_widget_scaling(self._desired_height)) @@ -183,10 +185,10 @@ class CTkCheckBox(CTkBaseClass): self._text = kwargs.pop("text") self._text_label.configure(text=self._text) - if "text_font" in kwargs: - self._text_font = kwargs.pop("text_font") + if "font" in kwargs: + self._font = kwargs.pop("font") if self._text_label is not None: - self._text_label.configure(font=self._apply_font_scaling(self._text_font)) + self._text_label.configure(font=self._apply_font_scaling(self._font)) if "state" in kwargs: self._state = kwargs.pop("state") @@ -229,6 +231,44 @@ class CTkCheckBox(CTkBaseClass): super().configure(require_redraw=require_redraw, **kwargs) + def cget(self, attribute_name: str) -> any: + if attribute_name == "corner_radius": + return self._corner_radius + elif attribute_name == "border_width": + return self._border_width + + elif attribute_name == "fg_color": + return self._fg_color + elif attribute_name == "hover_color": + return self._hover_color + elif attribute_name == "border_color": + return self._border_color + elif attribute_name == "checkmark_color": + return self._checkmark_color + elif attribute_name == "text_color": + return self._text_color + elif attribute_name == "text_color_disabled": + return self._text_color_disabled + + elif attribute_name == "text": + return self._text + elif attribute_name == "font": + return self._font + elif attribute_name == "textvariable": + return self._textvariable + elif attribute_name == "state": + return self._state + elif attribute_name == "hover": + return self._hover + elif attribute_name == "onvalue": + return self._onvalue + elif attribute_name == "offvalue": + return self._offvalue + elif attribute_name == "variable": + return self._variable + else: + return super().cget(attribute_name) + def _set_cursor(self): if Settings.cursor_manipulation_enabled: if self._state == tkinter.DISABLED: diff --git a/customtkinter/widgets/ctk_combobox.py b/customtkinter/widgets/ctk_combobox.py index 47d23d2..6cb6f77 100644 --- a/customtkinter/widgets/ctk_combobox.py +++ b/customtkinter/widgets/ctk_combobox.py @@ -9,6 +9,8 @@ from ..settings import Settings from ..draw_engine import DrawEngine from .widget_base_class import CTkBaseClass +from .widget_helper_functions import filter_dict_by_set + class CTkComboBox(CTkBaseClass): """ @@ -17,27 +19,29 @@ class CTkComboBox(CTkBaseClass): """ def __init__(self, *args, + width: int = 140, + height: int = 28, + corner_radius: Union[int, str] = "default_theme", + border_width: Union[int, str] = "default_theme", + bg_color: Union[str, Tuple[str, str], None] = None, fg_color: Union[str, Tuple[str, str]] = "default_theme", border_color: Union[str, Tuple[str, str]] = "default_theme", button_color: Union[str, Tuple[str, str]] = "default_theme", button_hover_color: Union[str, Tuple[str, str]] = "default_theme", - dropdown_color: Union[str, Tuple[str, str]] = "default_theme", + dropdown_fg_color: Union[str, Tuple[str, str]] = "default_theme", dropdown_hover_color: Union[str, Tuple[str, str]] = "default_theme", dropdown_text_color: Union[str, Tuple[str, str]] = "default_theme", - variable: tkinter.Variable = None, - values: List[str] = None, - command: Callable = None, - width: int = 140, - height: int = 28, - corner_radius: int = "default_theme", - border_width: int = "default_theme", - text_font: any = "default_theme", - dropdown_text_font: any = "default_theme", text_color: Union[str, Tuple[str, str]] = "default_theme", text_color_disabled: Union[str, Tuple[str, str]] = "default_theme", - hover: bool = True, + + font: any = "default_theme", + dropdown_text_font: any = "default_theme", + values: List[str] = None, state: str = tkinter.NORMAL, + hover: bool = True, + variable: tkinter.Variable = None, + command: Callable = None, **kwargs): # transfer basic functionality (_bg_color, size, _appearance_mode, scaling) to CTkBaseClass @@ -56,11 +60,11 @@ class CTkComboBox(CTkBaseClass): # text and font self._text_color = ThemeManager.theme["color"]["text"] 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._text_font = (ThemeManager.theme["text"]["font"], ThemeManager.theme["text"]["size"]) if text_font == "default_theme" else text_font + self._font = (ThemeManager.theme["text"]["font"], ThemeManager.theme["text"]["size"]) if font == "default_theme" else font # callback and hover functionality self._command = command - self._textvariable = variable + self._variable = variable self._state = state self._hover = hover @@ -72,10 +76,10 @@ class CTkComboBox(CTkBaseClass): self._dropdown_menu = DropdownMenu(master=self, values=self._values, command=self._dropdown_callback, - fg_color=dropdown_color, + fg_color=dropdown_fg_color, hover_color=dropdown_hover_color, text_color=dropdown_text_color, - text_font=dropdown_text_font) + font=dropdown_text_font) # configure grid system (1x1) self.grid_rowconfigure(0, weight=1) @@ -93,7 +97,7 @@ class CTkComboBox(CTkBaseClass): width=1, bd=0, highlightthickness=0, - font=self._apply_font_scaling(self._text_font)) + font=self._apply_font_scaling(self._font)) left_section_width = self._current_width - self._current_height self._entry.grid(row=0, column=0, rowspan=1, columnspan=1, sticky="ew", padx=(max(self._apply_widget_scaling(self._corner_radius), self._apply_widget_scaling(3)), @@ -116,15 +120,15 @@ class CTkComboBox(CTkBaseClass): self._canvas.tag_bind("dropdown_arrow", "", self._clicked) self.bind('', self._update_dimensions_event) - if self._textvariable is not None: - self._entry.configure(textvariable=self._textvariable) + if self._variable is not None: + self._entry.configure(textvariable=self._variable) def _set_scaling(self, *args, **kwargs): super()._set_scaling(*args, **kwargs) # change entry font size and grid padding left_section_width = self._current_width - self._current_height - self._entry.configure(font=self._apply_font_scaling(self._text_font)) + self._entry.configure(font=self._apply_font_scaling(self._font)) self._entry.grid(row=0, column=0, rowspan=1, columnspan=1, 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)))) @@ -210,16 +214,16 @@ class CTkComboBox(CTkBaseClass): self._text_color = kwargs.pop("text_color") require_redraw = True - if "text_font" in kwargs: - self._text_font = kwargs.pop("text_font") - self._entry.configure(font=self._apply_font_scaling(self._text_font)) + if "font" in kwargs: + self._font = kwargs.pop("font") + self._entry.configure(font=self._apply_font_scaling(self._font)) if "command" in kwargs: self._command = kwargs.pop("command") if "variable" in kwargs: - self._textvariable = kwargs.pop("variable") - self._entry.configure(textvariable=self._textvariable) + self._variable = kwargs.pop("variable") + self._entry.configure(textvariable=self._variable) if "width" in kwargs: self._set_dimensions(width=kwargs.pop("width")) @@ -231,8 +235,8 @@ class CTkComboBox(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_fg_color" in kwargs: + self._dropdown_menu.configure(fg_color=kwargs.pop("dropdown_fg_color")) if "dropdown_hover_color" in kwargs: self._dropdown_menu.configure(hover_color=kwargs.pop("dropdown_hover_color")) @@ -245,6 +249,48 @@ class CTkComboBox(CTkBaseClass): super().configure(require_redraw=require_redraw, **kwargs) + def cget(self, attribute_name: str) -> any: + if attribute_name == "corner_radius": + return self._corner_radius + elif attribute_name == "border_width": + return self._border_width + + elif attribute_name == "fg_color": + return self._fg_color + elif attribute_name == "border_color": + return self._border_color + elif attribute_name == "button_color": + return self._button_color + elif attribute_name == "button_hover_color": + return self._button_hover_color + elif attribute_name == "dropdown_fg_color": + return self._dropdown_menu.cget("fg_color") + elif attribute_name == "dropdown_hover_color": + return self._dropdown_menu.cget("hover_color") + elif attribute_name == "dropdown_text_color": + return self._dropdown_menu.cget("text_color") + elif attribute_name == "text_color": + return self._text_color + elif attribute_name == "text_color_disabled": + return self._text_color_disabled + + elif attribute_name == "font": + return self._font + elif attribute_name == "dropdown_text_font": + return self._dropdown_menu.cget("text_font") + elif attribute_name == "values": + return self._values + elif attribute_name == "state": + return self._state + elif attribute_name == "hover": + return self._hover + elif attribute_name == "variable": + return self._variable + elif attribute_name == "command": + return self._command + 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: diff --git a/customtkinter/widgets/ctk_entry.py b/customtkinter/widgets/ctk_entry.py index 94422f5..88f7d32 100644 --- a/customtkinter/widgets/ctk_entry.py +++ b/customtkinter/widgets/ctk_entry.py @@ -6,6 +6,8 @@ from ..theme_manager import ThemeManager from ..draw_engine import DrawEngine from .widget_base_class import CTkBaseClass +from .widget_helper_functions import * + class CTkEntry(CTkBaseClass): """ @@ -13,23 +15,33 @@ class CTkEntry(CTkBaseClass): For detailed information check out the documentation. """ + # attributes that are passed to and managed by the tkinter entry only: + _valid_tk_entry_attributes = {"exportselection", "font", "highlightbackground", + "highlightcolor", "insertbackground", "insertborderwidth", + "insertofftime", "insertontime", "insertwidth", + "justify", "selectbackground", "selectborderwidth", + "selectforeground", "show", "takefocus", "validate", + "validatecommand", "xscrollcommand"} + def __init__(self, *args, - bg_color: Union[str, Tuple[str, str], None] = None, - fg_color: Union[str, Tuple[str, str], None] = "default_theme", - text_color: Union[str, Tuple[str, str]] = "default_theme", - placeholder_text_color: Union[str, Tuple[str, str]] = "default_theme", - text_font: Union[str, Tuple[str, str]] = "default_theme", - placeholder_text: str = None, - corner_radius: int = "default_theme", - border_width: int = "default_theme", - border_color: Union[str, Tuple[str, str]] = "default_theme", width: int = 140, height: int = 28, - state: str = tkinter.NORMAL, + corner_radius: int = "default_theme", + border_width: int = "default_theme", + + bg_color: Union[str, Tuple[str, str], None] = None, + fg_color: Union[str, Tuple[str, str], None] = "default_theme", + border_color: Union[str, Tuple[str, str]] = "default_theme", + text_color: Union[str, Tuple[str, str]] = "default_theme", + placeholder_text_color: Union[str, Tuple[str, str]] = "default_theme", + textvariable: tkinter.Variable = None, + placeholder_text: str = None, + font: Union[str, Tuple[str, str]] = "default_theme", + state: str = tkinter.NORMAL, **kwargs): - # transfer basic functionality (_bg_color, size, _appearance_mode, scaling) to CTkBaseClass + # transfer basic functionality (bg_color, size, appearance_mode, scaling) to CTkBaseClass if "master" in kwargs: super().__init__(*args, bg_color=bg_color, width=width, height=height, master=kwargs.pop("master")) else: @@ -43,7 +55,7 @@ class CTkEntry(CTkBaseClass): self._fg_color = ThemeManager.theme["color"]["entry"] if fg_color == "default_theme" else fg_color self._text_color = ThemeManager.theme["color"]["text"] if text_color == "default_theme" else text_color self._placeholder_text_color = ThemeManager.theme["color"]["entry_placeholder_text"] if placeholder_text_color == "default_theme" else placeholder_text_color - self._text_font = (ThemeManager.theme["text"]["font"], ThemeManager.theme["text"]["size"]) if text_font == "default_theme" else text_font + self._font = (ThemeManager.theme["text"]["font"], ThemeManager.theme["text"]["size"]) if font == "default_theme" else font self._border_color = ThemeManager.theme["color"]["entry_border"] if border_color == "default_theme" else border_color # shape @@ -71,10 +83,10 @@ class CTkEntry(CTkBaseClass): bd=0, width=1, highlightthickness=0, - font=self._apply_font_scaling(self._text_font), + font=self._apply_font_scaling(self._font), state=self._state, textvariable=self._textvariable, - **kwargs) + **filter_dict_by_set(kwargs, self._valid_tk_entry_attributes)) self._entry.grid(column=0, row=0, sticky="nswe", padx=self._apply_widget_scaling(self._corner_radius) if self._corner_radius >= 6 else self._apply_widget_scaling(6), pady=(self._apply_widget_scaling(self._border_width), self._apply_widget_scaling(self._border_width + 1))) @@ -89,7 +101,7 @@ class CTkEntry(CTkBaseClass): def _set_scaling(self, *args, **kwargs): super()._set_scaling(*args, **kwargs) - self._entry.configure(font=self._apply_font_scaling(self._text_font)) + self._entry.configure(font=self._apply_font_scaling(self._font)) self._entry.grid(column=0, row=0, sticky="we", padx=self._apply_widget_scaling(self._corner_radius) if self._corner_radius >= 6 else self._apply_widget_scaling(6)) @@ -144,6 +156,8 @@ class CTkEntry(CTkBaseClass): return self.configure(*args, **kwargs) def configure(self, require_redraw=False, **kwargs): + self._entry.configure(**filter_dict_by_set(kwargs, self._valid_tk_entry_attributes)) + if "state" in kwargs: self._state = kwargs.pop("state") self._entry.configure(state=self._state) @@ -193,9 +207,9 @@ class CTkEntry(CTkBaseClass): self._textvariable = kwargs.pop("textvariable") self._entry.configure(textvariable=self._textvariable) - if "text_font" in kwargs: - self._text_font = kwargs.pop("text_font") - self._entry.configure(font=self._apply_font_scaling(self._text_font)) + if "font" in kwargs: + self._font = kwargs.pop("font") + self._entry.configure(font=self._apply_font_scaling(self._font)) if "show" in kwargs: if self._placeholder_text_active: @@ -203,12 +217,40 @@ class CTkEntry(CTkBaseClass): else: self._entry.configure(show=kwargs.pop("show")) - if "_bg_color" in kwargs: - super().configure(bg_color=kwargs.pop("_bg_color"), require_redraw=require_redraw) - else: - super().configure(require_redraw=require_redraw) + super().configure(require_redraw=require_redraw, **kwargs) - self._entry.configure(**kwargs) # pass remaining kwargs to entry + def cget(self, attribute_name: str) -> any: + if attribute_name == "corner_radius": + return self._corner_radius + elif attribute_name == "border_width": + return self._border_width + + elif attribute_name == "fg_color": + return self._fg_color + elif attribute_name == "border_color": + return self._border_color + elif attribute_name == "text_color": + return self._text_color + elif attribute_name == "placeholder_text_color": + return self._placeholder_text_color + + elif attribute_name == "textvariable": + return self._textvariable + elif attribute_name == "placeholder_text": + return self._placeholder_text + elif attribute_name == "text_font": + return self._font + elif attribute_name == "state": + return self._state + + elif attribute_name in ["exportselection", "font", "highlightbackground", + "highlightcolor", "insertbackground", + "insertborderwidth", "insertofftime", "insertontime", + "insertwidth", "justify", "selectbackground", + "selectborderwidth", "selectforeground", "show", + "takefocus", "validate", "validatecommand", + "xscrollcommand"]: + return self._entry.cget(attribute_name) def bind(self, *args, **kwargs): self._entry.bind(*args, **kwargs) diff --git a/customtkinter/widgets/ctk_frame.py b/customtkinter/widgets/ctk_frame.py index ea0c6fa..014d428 100644 --- a/customtkinter/widgets/ctk_frame.py +++ b/customtkinter/widgets/ctk_frame.py @@ -15,13 +15,15 @@ class CTkFrame(CTkBaseClass): """ def __init__(self, *args, + width: int = 200, + height: int = 200, + corner_radius: Union[int, str] = "default_theme", + border_width: Union[int, str] = "default_theme", + bg_color: Union[str, Tuple[str, str], None] = None, fg_color: Union[str, Tuple[str, str], None] = "default_theme", border_color: Union[str, Tuple[str, str]] = "default_theme", - border_width: Union[int, str] = "default_theme", - corner_radius: Union[int, str] = "default_theme", - width: int = 200, - height: int = 200, + overwrite_preferred_drawing_method: str = None, **kwargs): @@ -144,3 +146,16 @@ class CTkFrame(CTkBaseClass): self._set_dimensions(height=kwargs.pop("height")) super().configure(require_redraw=require_redraw, **kwargs) + + def cget(self, attribute_name: str) -> any: + if attribute_name == "corner_radius": + return self._corner_radius + elif attribute_name == "border_width": + return self._border_width + + elif attribute_name == "fg_color": + return self._fg_color + elif attribute_name == "border_color": + return self._border_color + else: + return super().cget(attribute_name) diff --git a/customtkinter/widgets/ctk_label.py b/customtkinter/widgets/ctk_label.py index 9043921..cbad9d3 100644 --- a/customtkinter/widgets/ctk_label.py +++ b/customtkinter/widgets/ctk_label.py @@ -13,15 +13,20 @@ class CTkLabel(CTkBaseClass): For detailed information check out the documentation. """ + # attributes that are passed to and managed by the tkinter entry only: + _valid_tk_label_attributes = {"compound", "cursor", ""} + def __init__(self, *args, + width: int = 140, + height: int = 28, + corner_radius: Union[int, str] = "default_theme", + bg_color: Union[str, Tuple[str, str], None] = None, fg_color: Union[str, Tuple[str, str], None] = "default_theme", text_color: Union[str, Tuple[str, str]] = "default_theme", - corner_radius: Union[int, str] = "default_theme", - width: int = 140, - height: int = 28, + text: str = "CTkLabel", - text_font: any = "default_theme", + font: any = "default_theme", anchor: str = "center", # label anchor: center, n, e, s, w **kwargs): @@ -41,7 +46,7 @@ class CTkLabel(CTkBaseClass): # text self._anchor = anchor self._text = text - self._text_font = (ThemeManager.theme["text"]["font"], ThemeManager.theme["text"]["size"]) if text_font == "default_theme" else text_font + self._font = (ThemeManager.theme["text"]["font"], ThemeManager.theme["text"]["size"]) if font == "default_theme" else font # configure grid system (1x1) self.grid_rowconfigure(0, weight=1) @@ -59,7 +64,7 @@ class CTkLabel(CTkBaseClass): bd=0, anchor=self._anchor, text=self._text, - font=self._apply_font_scaling(self._text_font), + font=self._apply_font_scaling(self._font), **kwargs) text_label_grid_sticky = self._anchor if self._anchor != "center" else "" self._text_label.grid(row=0, column=0, padx=self._apply_widget_scaling(self._corner_radius), @@ -72,7 +77,7 @@ class CTkLabel(CTkBaseClass): super()._set_scaling(*args, **kwargs) self._canvas.configure(width=self._apply_widget_scaling(self._desired_width), height=self._apply_widget_scaling(self._desired_height)) - self._text_label.configure(font=self._apply_font_scaling(self._text_font)) + self._text_label.configure(font=self._apply_font_scaling(self._font)) text_label_grid_sticky = self._anchor if self._anchor != "center" else "" self._text_label.grid(row=0, column=0, padx=self._apply_widget_scaling(self._corner_radius), sticky=text_label_grid_sticky) @@ -124,9 +129,9 @@ class CTkLabel(CTkBaseClass): self._text = kwargs.pop("text") self._text_label.configure(text=self._text) - if "text_font" in kwargs: - self._text_font = kwargs.pop("text_font") - self._text_label.configure(font=self._apply_font_scaling(self._text_font)) + if "font" in kwargs: + self._font = kwargs.pop("font") + self._text_label.configure(font=self._apply_font_scaling(self._font)) if "fg_color" in kwargs: self._fg_color = kwargs.pop("fg_color") @@ -148,3 +153,21 @@ class CTkLabel(CTkBaseClass): super().configure(require_redraw=require_redraw) self._text_label.configure(**kwargs) # pass remaining kwargs to label + + def cget(self, attribute_name: str) -> any: + if attribute_name == "corner_radius": + return self._corner_radius + + elif attribute_name == "fg_color": + return self._fg_color + elif attribute_name == "text_color": + return self._text_color + + elif attribute_name == "text": + return self._text + elif attribute_name == "font": + return self._font + elif attribute_name == "anchor": + return self._anchor + else: + return super().cget(attribute_name) diff --git a/customtkinter/widgets/ctk_optionmenu.py b/customtkinter/widgets/ctk_optionmenu.py index a46e2af..da58acd 100644 --- a/customtkinter/widgets/ctk_optionmenu.py +++ b/customtkinter/widgets/ctk_optionmenu.py @@ -12,30 +12,32 @@ from .dropdown_menu import DropdownMenu class CTkOptionMenu(CTkBaseClass): """ - Optionemnu with rounded corners, dropdown menu, variable support, command. + Optionmenu with rounded corners, dropdown menu, variable support, command. For detailed information check out the documentation. """ def __init__(self, *args, + width: int = 140, + height: int = 28, + corner_radius: Union[int, str] = "default_theme", + bg_color: Union[str, Tuple[str, str], None] = None, fg_color: Union[str, Tuple[str, str]] = "default_theme", button_color: Union[str, Tuple[str, str]] = "default_theme", button_hover_color: Union[str, Tuple[str, str]] = "default_theme", text_color: Union[str, Tuple[str, str]] = "default_theme", text_color_disabled: Union[str, Tuple[str, str]] = "default_theme", - dropdown_color: Union[str, Tuple[str, str]] = "default_theme", + dropdown_fg_color: Union[str, Tuple[str, str]] = "default_theme", dropdown_hover_color: Union[str, Tuple[str, str]] = "default_theme", dropdown_text_color: Union[str, Tuple[str, str]] = "default_theme", - variable: tkinter.Variable = None, - values: list = None, - command: Callable = None, - width: int = 140, - height: int = 28, - corner_radius: Union[int, str] = "default_theme", - text_font: any = "default_theme", + + font: any = "default_theme", dropdown_text_font: any = "default_theme", - hover: bool = True, + values: list = None, + variable: tkinter.Variable = None, state: str = tkinter.NORMAL, + hover: bool = True, + command: Callable = None, dynamic_resizing: bool = True, **kwargs): @@ -53,7 +55,7 @@ class CTkOptionMenu(CTkBaseClass): # text and font self._text_color = ThemeManager.theme["color"]["text"] 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._text_font = (ThemeManager.theme["text"]["font"], ThemeManager.theme["text"]["size"]) if text_font == "default_theme" else text_font + 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 @@ -78,10 +80,10 @@ class CTkOptionMenu(CTkBaseClass): self._dropdown_menu = DropdownMenu(master=self, values=self._values, command=self._dropdown_callback, - fg_color=dropdown_color, + fg_color=dropdown_fg_color, hover_color=dropdown_hover_color, text_color=dropdown_text_color, - text_font=dropdown_text_font) + font=dropdown_text_font) # configure grid system (1x1) self.grid_rowconfigure(0, weight=1) @@ -96,7 +98,7 @@ class CTkOptionMenu(CTkBaseClass): left_section_width = self._current_width - self._current_height self._text_label = tkinter.Label(master=self, - font=self._apply_font_scaling(self._text_font), + font=self._apply_font_scaling(self._font), anchor="w", text=self._current_value) self._text_label.grid(row=0, column=0, sticky="w", @@ -137,7 +139,7 @@ class CTkOptionMenu(CTkBaseClass): # 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._text_font)) + self._text_label.configure(font=self._apply_font_scaling(self._font)) 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)))) @@ -215,9 +217,9 @@ class CTkOptionMenu(CTkBaseClass): self._text_color = kwargs.pop("text_color") require_redraw = True - if "text_font" in kwargs: - self._text_font = kwargs.pop("text_font") - self._text_label.configure(font=self._apply_font_scaling(self._text_font)) + if "font" in kwargs: + self._font = kwargs.pop("font") + self._text_label.configure(font=self._apply_font_scaling(self._font)) if "command" in kwargs: self._command = kwargs.pop("command") @@ -267,6 +269,46 @@ class CTkOptionMenu(CTkBaseClass): super().configure(require_redraw=require_redraw, **kwargs) + def cget(self, attribute_name: str) -> any: + if attribute_name == "corner_radius": + return self._corner_radius + + elif attribute_name == "fg_color": + return self._fg_color + elif attribute_name == "button_color": + return self._button_color + elif attribute_name == "button_hover_color": + return self._button_hover_color + elif attribute_name == "text_color": + return self._text_color + elif attribute_name == "text_color_disabled": + return self._text_color_disabled + elif attribute_name == "dropdown_fg_color": + return self._dropdown_menu.cget("fg_color") + elif attribute_name == "dropdown_hover_color": + return self._dropdown_menu.cget("hover_color") + elif attribute_name == "dropdown_text_color": + return self._dropdown_menu.cget("text_color") + + elif attribute_name == "font": + return self._font + elif attribute_name == "dropdown_text_font": + return self._dropdown_menu.cget("text_font") + elif attribute_name == "values": + return self._values + elif attribute_name == "variable": + return self._variable + elif attribute_name == "state": + return self._state + elif attribute_name == "hover": + return self._hover + elif attribute_name == "command": + return self._command + elif attribute_name == "dynamic_resizing": + return self._dynamic_resizing + else: + return super().cget(attribute_name) + def _open_dropdown_menu(self): self._dropdown_menu.open(self.winfo_rootx(), self.winfo_rooty() + self._apply_widget_scaling(self._current_height + 0)) diff --git a/customtkinter/widgets/ctk_progressbar.py b/customtkinter/widgets/ctk_progressbar.py index 20bc70f..6a41af9 100644 --- a/customtkinter/widgets/ctk_progressbar.py +++ b/customtkinter/widgets/ctk_progressbar.py @@ -16,16 +16,18 @@ class CTkProgressBar(CTkBaseClass): """ def __init__(self, *args, - bg_color: Union[str, Tuple[str, str], None] = None, - border_color: Union[str, Tuple[str, str]] = "default_theme", - fg_color: Union[str, Tuple[str, str]] = "default_theme", - progress_color: Union[str, Tuple[str, str]] = "default_theme", - corner_radius: Union[str, Tuple[str, str]] = "default_theme", width: Union[int, str] = "default_init", height: Union[int, str] = "default_init", + corner_radius: Union[str, Tuple[str, str]] = "default_theme", border_width: Union[int, str] = "default_theme", + + bg_color: Union[str, Tuple[str, str], None] = None, + fg_color: Union[str, Tuple[str, str]] = "default_theme", + border_color: Union[str, Tuple[str, str]] = "default_theme", + progress_color: Union[str, Tuple[str, str]] = "default_theme", + variable: tkinter.Variable = None, - orient: str = "horizontal", + orientation: str = "horizontal", mode: str = "determinate", determinate_speed: float = 1, indeterminate_speed: float = 1, @@ -33,12 +35,12 @@ class CTkProgressBar(CTkBaseClass): # set default dimensions according to orientation if width == "default_init": - if orient.lower() == "vertical": + if orientation.lower() == "vertical": width = 8 else: width = 200 if height == "default_init": - if orient.lower() == "vertical": + if orientation.lower() == "vertical": height = 200 else: height = 8 @@ -65,7 +67,7 @@ class CTkProgressBar(CTkBaseClass): self._indeterminate_width: float = 0.4 # range 0-1 self._indeterminate_speed = indeterminate_speed # range 0-1 to travel in 50ms self._loop_running: bool = False - self._orient = orient + self._orientation = orientation self._mode = mode # "determinate" or "indeterminate" self.grid_rowconfigure(0, weight=1) @@ -110,9 +112,9 @@ class CTkProgressBar(CTkBaseClass): super().destroy() def _draw(self, no_color_updates=False): - if self._orient.lower() == "horizontal": + if self._orientation.lower() == "horizontal": orientation = "w" - elif self._orient.lower() == "vertical": + elif self._orientation.lower() == "vertical": orientation = "s" else: orientation = "w" @@ -200,6 +202,32 @@ class CTkProgressBar(CTkBaseClass): super().configure(require_redraw=require_redraw, **kwargs) + def cget(self, attribute_name: str) -> any: + if attribute_name == "corner_radius": + return self._corner_radius + elif attribute_name == "border_width": + return self._border_width + + elif attribute_name == "fg_color": + return self._fg_color + elif attribute_name == "border_color": + return self._border_color + elif attribute_name == "progress_color": + return self._progress_color + + elif attribute_name == "variable": + return self._variable + elif attribute_name == "orientation": + return self._orientation + elif attribute_name == "mode": + return self._mode + elif attribute_name == "determinate_speed": + return self._determinate_speed + elif attribute_name == "indeterminate_speed": + return self._indeterminate_speed + else: + return super().cget(attribute_name) + def _variable_callback(self, var_name, index, mode): if not self._variable_callback_blocked: self.set(self._variable.get(), from_variable_callback=True) diff --git a/customtkinter/widgets/ctk_radiobutton.py b/customtkinter/widgets/ctk_radiobutton.py index bb8afc6..3768da2 100644 --- a/customtkinter/widgets/ctk_radiobutton.py +++ b/customtkinter/widgets/ctk_radiobutton.py @@ -16,25 +16,27 @@ class CTkRadioButton(CTkBaseClass): """ def __init__(self, *args, + width: int = 22, + height: int = 22, + corner_radius: Union[int, str] = "default_theme", + border_width_unchecked: Union[int, str] = "default_theme", + border_width_checked: Union[int, str] = "default_theme", + bg_color: Union[str, Tuple[str, str], None] = None, fg_color: Union[str, Tuple[str, str]] = "default_theme", hover_color: Union[str, Tuple[str, str]] = "default_theme", border_color: Union[str, Tuple[str, str]] = "default_theme", - border_width_unchecked: Union[int, str] = "default_theme", - border_width_checked: Union[int, str] = "default_theme", - width: int = 22, - height: int = 22, - corner_radius: Union[int, str] = "default_theme", - text_font: any = "default_theme", text_color: Union[str, Tuple[str, str]] = "default_theme", - text: str = "CTkRadioButton", text_color_disabled: Union[str, Tuple[str, str]] = "default_theme", + + text: str = "CTkRadioButton", + font: any = "default_theme", + textvariable: tkinter.Variable = None, + variable: tkinter.Variable = None, + value: Union[int, str] = 0, + state: str = tkinter.NORMAL, hover: bool = True, command: Callable = None, - state: str = tkinter.NORMAL, - value: Union[int, str] = 0, - variable: tkinter.Variable = None, - textvariable: tkinter.Variable = None, **kwargs): # transfer basic functionality (_bg_color, size, _appearance_mode, scaling) to CTkBaseClass @@ -56,7 +58,7 @@ class CTkRadioButton(CTkBaseClass): self._text_label: Union[tkinter.Label, None] = None self._text_color = ThemeManager.theme["color"]["text"] if text_color == "default_theme" else text_color self._text_color_disabled = ThemeManager.theme["color"]["text_disabled"] if text_color_disabled == "default_theme" else text_color_disabled - self._text_font = (ThemeManager.theme["text"]["font"], ThemeManager.theme["text"]["size"]) if text_font == "default_theme" else text_font + self._font = (ThemeManager.theme["text"]["font"], ThemeManager.theme["text"]["size"]) if font == "default_theme" else font # callback and control variables self._command = command @@ -95,7 +97,7 @@ class CTkRadioButton(CTkBaseClass): bd=0, text=self._text, justify=tkinter.LEFT, - font=self._apply_font_scaling(self._text_font), + font=self._apply_font_scaling(self._font), textvariable=self._textvariable) self._text_label.grid(row=0, column=2, padx=0, pady=0, sticky="w") self._text_label["anchor"] = "w" @@ -115,7 +117,7 @@ class CTkRadioButton(CTkBaseClass): super()._set_scaling(*args, **kwargs) self.grid_columnconfigure(1, weight=0, minsize=self._apply_widget_scaling(6)) - self._text_label.configure(font=self._apply_font_scaling(self._text_font)) + self._text_label.configure(font=self._apply_font_scaling(self._font)) self._bg_canvas.configure(width=self._apply_widget_scaling(self._desired_width), height=self._apply_widget_scaling(self._desired_height)) self._canvas.configure(width=self._apply_widget_scaling(self._desired_width), height=self._apply_widget_scaling(self._desired_height)) @@ -164,9 +166,9 @@ class CTkRadioButton(CTkBaseClass): self._text = kwargs.pop("text") self._text_label.configure(text=self._text) - if "text_font" in kwargs: - self._text_font = kwargs.pop("text_font") - self._text_label.configure(font=self._apply_font_scaling(self._text_font)) + if "font" in kwargs: + self._font = kwargs.pop("font") + self._text_label.configure(font=self._apply_font_scaling(self._font)) if "state" in kwargs: self._state = kwargs.pop("state") @@ -213,6 +215,44 @@ class CTkRadioButton(CTkBaseClass): super().configure(require_redraw=require_redraw, **kwargs) + def cget(self, attribute_name: str) -> any: + if attribute_name == "corner_radius": + return self._corner_radius + elif attribute_name == "border_width_unchecked": + return self._border_width_unchecked + elif attribute_name == "border_width_checked": + return self._border_width_checked + + elif attribute_name == "fg_color": + return self._fg_color + elif attribute_name == "hover_color": + return self._hover_color + elif attribute_name == "border_color": + return self._border_color + elif attribute_name == "text_color": + return self._text_color + elif attribute_name == "text_color_disabled": + return self._text_color_disabled + + elif attribute_name == "text": + return self._text + elif attribute_name == "font": + return self._font + elif attribute_name == "textvariable": + return self._textvariable + elif attribute_name == "variable": + return self._variable + elif attribute_name == "value": + return self._value + elif attribute_name == "state": + return self._state + elif attribute_name == "hover": + return self._hover + elif attribute_name == "command": + return self._command + else: + return super().cget(attribute_name) + def _set_cursor(self): if Settings.cursor_manipulation_enabled: if self._state == tkinter.DISABLED: diff --git a/customtkinter/widgets/ctk_scrollbar.py b/customtkinter/widgets/ctk_scrollbar.py index 2b7b7c3..65582a7 100644 --- a/customtkinter/widgets/ctk_scrollbar.py +++ b/customtkinter/widgets/ctk_scrollbar.py @@ -15,18 +15,20 @@ class CTkScrollbar(CTkBaseClass): """ def __init__(self, *args, + width: Union[int, str] = "default_init", + height: Union[int, str] = "default_init", + corner_radius: Union[int, str] = "default_theme", + border_spacing: Union[int, str] = "default_theme", + minimum_pixel_length: int = 20, + bg_color: Union[str, Tuple[str, str], None] = None, fg_color: Union[str, Tuple[str, str], None] = "default_theme", scrollbar_color: Union[str, Tuple[str, str]] = "default_theme", scrollbar_hover_color: Union[str, Tuple[str, str]] = "default_theme", - border_spacing: Union[int, str] = "default_theme", - corner_radius: Union[int, str] = "default_theme", - width: Union[int, str] = "default_init", - height: Union[int, str] = "default_init", - minimum_pixel_length: int = 20, - orientation: str = "vertical", - command: Callable = None, + hover: bool = True, + command: Callable = None, + orientation: str = "vertical", **kwargs): # set default dimensions according to orientation @@ -183,6 +185,30 @@ class CTkScrollbar(CTkBaseClass): super().configure(require_redraw=require_redraw, **kwargs) + def cget(self, attribute_name: str) -> any: + if attribute_name == "corner_radius": + return self._corner_radius + elif attribute_name == "border_spacing": + return self._border_spacing + elif attribute_name == "minimum_pixel_length": + return self._minimum_pixel_length + + elif attribute_name == "fg_color": + return self._fg_color + elif attribute_name == "scrollbar_color": + return self._scrollbar_color + elif attribute_name == "scrollbar_hover_color": + return self._scrollbar_hover_color + + elif attribute_name == "hover": + return self._hover + elif attribute_name == "command": + return self._command + elif attribute_name == "orientation": + return self._orientation + else: + return super().cget(attribute_name) + def _on_enter(self, event=0): if self._hover is True: self._hover_state = True diff --git a/customtkinter/widgets/ctk_slider.py b/customtkinter/widgets/ctk_slider.py index f1d5bcf..60913b3 100644 --- a/customtkinter/widgets/ctk_slider.py +++ b/customtkinter/widgets/ctk_slider.py @@ -16,35 +16,37 @@ class CTkSlider(CTkBaseClass): """ def __init__(self, *args, - bg_color: Union[str, Tuple[str, str], None] = None, - border_color: Union[str, Tuple[str, str], None] = None, - fg_color: Union[str, Tuple[str, str]] = "default_theme", - progress_color: Union[str, Tuple[str, str], None] = "default_theme", - button_color: Union[str, Tuple[str, str]] = "default_theme", - button_hover_color: Union[str, Tuple[str, str]] = "default_theme", - from_: int = 0, - to: int = 1, - number_of_steps: Union[int, None] = None, width: Union[int, str] = "default_init", height: Union[int, str] = "default_init", corner_radius: Union[int, str] = "default_theme", button_corner_radius: Union[int, str] = "default_theme", border_width: Union[int, str] = "default_theme", button_length: Union[int, str] = "default_theme", + + bg_color: Union[str, Tuple[str, str], None] = None, + fg_color: Union[str, Tuple[str, str]] = "default_theme", + border_color: Union[str, Tuple[str, str], None] = None, + progress_color: Union[str, Tuple[str, str], None] = "default_theme", + button_color: Union[str, Tuple[str, str]] = "default_theme", + button_hover_color: Union[str, Tuple[str, str]] = "default_theme", + + from_: int = 0, + to: int = 1, + state: str = "normal", + number_of_steps: Union[int, None] = None, command: Callable = None, variable: tkinter.Variable = None, - orient: str = "horizontal", - state: str = "normal", + orientation: str = "horizontal", **kwargs): # set default dimensions according to orientation if width == "default_init": - if orient.lower() == "vertical": + if orientation.lower() == "vertical": width = 16 else: width = 200 if height == "default_init": - if orient.lower() == "vertical": + if orientation.lower() == "vertical": height = 200 else: height = 16 @@ -65,7 +67,7 @@ class CTkSlider(CTkBaseClass): self._border_width = ThemeManager.theme["shape"]["slider_border_width"] if border_width == "default_theme" else border_width self._button_length = ThemeManager.theme["shape"]["slider_button_length"] if button_length == "default_theme" else button_length self._value: float = 0.5 # initial value of slider in percent - self._orientation = orient + self._orientation = orientation self._hover_state: bool = False self._from_ = from_ self._to = to @@ -253,6 +255,44 @@ class CTkSlider(CTkBaseClass): super().configure(require_redraw=require_redraw, **kwargs) + def cget(self, attribute_name: str) -> any: + if attribute_name == "corner_radius": + return self._corner_radius + elif attribute_name == "button_corner_radius": + return self._button_corner_radius + elif attribute_name == "border_width": + return self._border_width + elif attribute_name == "button_length": + return self._button_length + + elif attribute_name == "fg_color": + return self._fg_color + elif attribute_name == "border_color": + return self._border_color + elif attribute_name == "progress_color": + return self._progress_color + elif attribute_name == "button_color": + return self._button_color + elif attribute_name == "button_hover_color": + return self._button_hover_color + + elif attribute_name == "from_": + return self._from_ + elif attribute_name == "to": + return self._to + elif attribute_name == "state": + return self._state + elif attribute_name == "number_of_steps": + return self._number_of_steps + elif attribute_name == "command": + return self._command + elif attribute_name == "variable": + return self._variable + elif attribute_name == "orientation": + return self._orientation + else: + return super().cget(attribute_name) + def _clicked(self, event=None): if self._state == "normal": if self._orientation.lower() == "horizontal": diff --git a/customtkinter/widgets/ctk_switch.py b/customtkinter/widgets/ctk_switch.py index 83acb1d..77dbf57 100644 --- a/customtkinter/widgets/ctk_switch.py +++ b/customtkinter/widgets/ctk_switch.py @@ -16,26 +16,28 @@ class CTkSwitch(CTkBaseClass): """ def __init__(self, *args, - bg_color: Union[str, Tuple[str, str], None] = None, - border_color: Union[str, Tuple[str, str], None] = None, - fg_color: Union[str, Tuple[str, str]] = "default_theme", - progress_color: Union[str, Tuple[str, str]] = "default_theme", - button_color: Union[str, Tuple[str, str]] = "default_theme", - button_hover_color: Union[str, Tuple[str, str]] = "default_theme", width: int = 36, height: int = 18, - text: str = "CTkSwitch", - text_font: any = "default_theme", - text_color: Union[str, Tuple[str, str]] = "default_theme", - text_color_disabled: Union[str, Tuple[str, str]] = "default_theme", corner_radius: Union[int, str] = "default_theme", border_width: Union[int, str] = "default_theme", button_length: Union[int, str] = "default_theme", - command: Callable = None, + + bg_color: Union[str, Tuple[str, str], None] = None, + fg_color: Union[str, Tuple[str, str]] = "default_theme", + border_color: Union[str, Tuple[str, str], None] = None, + progress_color: Union[str, Tuple[str, str]] = "default_theme", + button_color: Union[str, Tuple[str, str]] = "default_theme", + button_hover_color: Union[str, Tuple[str, str]] = "default_theme", + text_color: Union[str, Tuple[str, str]] = "default_theme", + text_color_disabled: Union[str, Tuple[str, str]] = "default_theme", + + text: str = "CTkSwitch", + font: any = "default_theme", + textvariable: tkinter.Variable = None, onvalue: Union[int, str] = 1, offvalue: Union[int, str] = 0, variable: tkinter.Variable = None, - textvariable: tkinter.Variable = None, + command: Callable = None, state: str = tkinter.NORMAL, **kwargs): @@ -54,7 +56,7 @@ class CTkSwitch(CTkBaseClass): # text self._text = text self._text_label = None - self._text_font = (ThemeManager.theme["text"]["font"], ThemeManager.theme["text"]["size"]) if text_font == "default_theme" else text_font + self._font = (ThemeManager.theme["text"]["font"], ThemeManager.theme["text"]["size"]) if font == "default_theme" else font # shape self._corner_radius = ThemeManager.theme["shape"]["switch_corner_radius"] if corner_radius == "default_theme" else corner_radius @@ -100,7 +102,7 @@ class CTkSwitch(CTkBaseClass): bd=0, text=self._text, justify=tkinter.LEFT, - font=self._apply_font_scaling(self._text_font), + font=self._apply_font_scaling(self._font), textvariable=self._textvariable) self._text_label.grid(row=0, column=2, padx=0, pady=0, sticky="w") self._text_label["anchor"] = "w" @@ -120,7 +122,7 @@ class CTkSwitch(CTkBaseClass): super()._set_scaling(*args, **kwargs) self.grid_columnconfigure(1, weight=0, minsize=self._apply_widget_scaling(6)) - self._text_label.configure(font=self._apply_font_scaling(self._text_font)) + self._text_label.configure(font=self._apply_font_scaling(self._font)) self._bg_canvas.configure(width=self._apply_widget_scaling(self._desired_width), height=self._apply_widget_scaling(self._desired_height)) self._canvas.configure(width=self._apply_widget_scaling(self._desired_width), height=self._apply_widget_scaling(self._desired_height)) @@ -213,9 +215,9 @@ class CTkSwitch(CTkBaseClass): self._text = kwargs.pop("text") self._text_label.configure(text=self._text) - if "text_font" in kwargs: - self._text_font = kwargs.pop("text_font") - self._text_label.configure(font=self._apply_font_scaling(self._text_font)) + if "font" in kwargs: + self._font = kwargs.pop("font") + self._text_label.configure(font=self._apply_font_scaling(self._font)) if "state" in kwargs: self._state = kwargs.pop("state") @@ -270,6 +272,48 @@ class CTkSwitch(CTkBaseClass): super().configure(require_redraw=require_redraw, **kwargs) + def cget(self, attribute_name: str) -> any: + if attribute_name == "corner_radius": + return self._corner_radius + elif attribute_name == "border_width": + return self._border_width + elif attribute_name == "button_length": + return self._button_length + + elif attribute_name == "fg_color": + return self._fg_color + elif attribute_name == "border_color": + return self._border_color + elif attribute_name == "progress_color": + return self._progress_color + elif attribute_name == "button_color": + return self._button_color + elif attribute_name == "button_hover_color": + return self._button_hover_color + elif attribute_name == "text_color": + return self._text_color + elif attribute_name == "text_color_disabled": + return self._text_color_disabled + + elif attribute_name == "text": + return self._text + elif attribute_name == "font": + return self._font + elif attribute_name == "textvariable": + return self._textvariable + elif attribute_name == "onvalue": + return self._onvalue + elif attribute_name == "offvalue": + return self._offvalue + elif attribute_name == "variable": + return self._variable + elif attribute_name == "command": + return self._command + elif attribute_name == "state": + return self._state + else: + return super().cget(attribute_name) + def toggle(self, event=None): if self._state is not tkinter.DISABLED: if self._check_state is True: diff --git a/customtkinter/widgets/ctk_textbox.py b/customtkinter/widgets/ctk_textbox.py index 4fef1f9..bcd28a1 100644 --- a/customtkinter/widgets/ctk_textbox.py +++ b/customtkinter/widgets/ctk_textbox.py @@ -1,5 +1,5 @@ import tkinter -from typing import Union, Tuple, Callable +from typing import Union, Tuple from .ctk_canvas import CTkCanvas from ..theme_manager import ThemeManager @@ -14,15 +14,17 @@ class CTkTextbox(CTkBaseClass): """ def __init__(self, *args, + width: int = 200, + height: int = 200, + corner_radius: Union[str, str] = "default_theme", + border_width: Union[str, str] = "default_theme", + bg_color: Union[str, Tuple[str, str], None] = None, fg_color: Union[str, Tuple[str, str], None] = "default_theme", border_color: Union[str, Tuple[str, str]] = "default_theme", - border_width: Union[str, str] = "default_theme", - corner_radius: Union[str, str] = "default_theme", - text_font: any = "default_theme", text_color: Union[str, str] = "default_theme", - width: int = 200, - height: int = 200, + + font: any = "default_theme", **kwargs): # transfer basic functionality (_bg_color, size, _appearance_mode, scaling) to CTkBaseClass @@ -41,7 +43,7 @@ class CTkTextbox(CTkBaseClass): self._border_width = ThemeManager.theme["shape"]["frame_border_width"] if border_width == "default_theme" else border_width # text - self._text_font = (ThemeManager.theme["text"]["font"], ThemeManager.theme["text"]["size"]) if text_font == "default_theme" else text_font + self._font = (ThemeManager.theme["text"]["font"], ThemeManager.theme["text"]["size"]) if font == "default_theme" else font # configure 1x1 grid self.grid_rowconfigure(0, weight=1) @@ -61,7 +63,7 @@ class CTkTextbox(CTkBaseClass): fg=ThemeManager.single_color(self._text_color, self._appearance_mode), width=0, height=0, - font=self._text_font, + font=self._font, highlightthickness=0, relief="flat", insertbackground=ThemeManager.single_color(("black", "white"), self._appearance_mode), @@ -75,7 +77,7 @@ class CTkTextbox(CTkBaseClass): def _set_scaling(self, *args, **kwargs): super()._set_scaling(*args, **kwargs) - self._textbox.configure(font=self._apply_font_scaling(self.text_font)) + self._textbox.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() @@ -147,9 +149,9 @@ class CTkTextbox(CTkBaseClass): if "height" in kwargs: self._set_dimensions(height=kwargs.pop("height")) - if "text_font" in kwargs: - self._text_font = kwargs.pop("text_font") - self._textbox.configure(font=self._apply_font_scaling(self._text_font)) + if "font" in kwargs: + self._font = kwargs.pop("font") + self._textbox.configure(font=self._apply_font_scaling(self._font)) if "font" in kwargs: raise ValueError("No attribute named font. Use text_font instead of font for CTk widgets") @@ -161,6 +163,24 @@ class CTkTextbox(CTkBaseClass): self._textbox.configure(**kwargs) + def cget(self, attribute_name: str) -> any: + if attribute_name == "corner_radius": + return self._corner_radius + elif attribute_name == "border_width": + return self._border_width + + elif attribute_name == "fg_color": + return self._fg_color + elif attribute_name == "border_color": + return self._border_color + elif attribute_name == "text_color": + return self._text_color + + elif attribute_name == "font": + return self._font + else: + return super().cget(attribute_name) + def yview(self, *args): return self._textbox.yview(*args) diff --git a/customtkinter/widgets/dropdown_menu.py b/customtkinter/widgets/dropdown_menu.py index 5c28a09..5a07970 100644 --- a/customtkinter/widgets/dropdown_menu.py +++ b/customtkinter/widgets/dropdown_menu.py @@ -12,13 +12,16 @@ from ..scaling_tracker import ScalingTracker class DropdownMenu(tkinter.Menu): def __init__(self, *args, min_character_width: int = 18, + fg_color: Union[str, Tuple[str, str]] = "default_theme", hover_color: Union[str, Tuple[str, str]] = "default_theme", text_color: Union[str, Tuple[str, str]] = "default_theme", - text_font: Union[str, Tuple[str, str]] = "default_theme", + + font: Union[str, Tuple[str, str]] = "default_theme", command: Callable = None, values: List[str] = None, **kwargs): + super().__init__(*args, **kwargs) ScalingTracker.add_widget(self._set_scaling, self) @@ -32,7 +35,7 @@ class DropdownMenu(tkinter.Menu): self._fg_color = ThemeManager.theme["color"]["dropdown_color"] if fg_color == "default_theme" else fg_color self._hover_color = ThemeManager.theme["color"]["dropdown_hover"] if hover_color == "default_theme" else hover_color self._text_color = ThemeManager.theme["color"]["text"] if text_color == "default_theme" else text_color - self._text_font = (ThemeManager.theme["text"]["font"], ThemeManager.theme["text"]["size"]) if text_font == "default_theme" else text_font + self._font = (ThemeManager.theme["text"]["font"], ThemeManager.theme["text"]["size"]) if font == "default_theme" else font self._configure_menu_for_platforms() @@ -46,7 +49,7 @@ class DropdownMenu(tkinter.Menu): if sys.platform == "darwin": self.configure(tearoff=False, - font=self._apply_font_scaling(self._text_font)) + font=self._apply_font_scaling(self._font)) elif sys.platform.startswith("win"): self.configure(tearoff=False, @@ -57,7 +60,7 @@ class DropdownMenu(tkinter.Menu): 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._text_font), + font=self._apply_font_scaling(self._font), cursor="hand2") else: @@ -69,7 +72,7 @@ class DropdownMenu(tkinter.Menu): 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._text_font)) + 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 """ @@ -102,14 +105,10 @@ class DropdownMenu(tkinter.Menu): else: # Linux self.tk_popup(int(x), int(y)) - def config(self, *args, **kwargs): - return self.configure(*args, **kwargs) + def config(self, **kwargs): + return self.configure(**kwargs) def configure(self, **kwargs): - if "values" in kwargs: - self._values = kwargs.pop("values") - self._add_menu_commands() - if "fg_color" in kwargs: self._fg_color = kwargs.pop("fg_color") self.configure(bg=ThemeManager.single_color(self._fg_color, self._appearance_mode)) @@ -122,11 +121,34 @@ class DropdownMenu(tkinter.Menu): self._text_color = kwargs.pop("text_color") self.configure(fg=ThemeManager.single_color(self._text_color, self._appearance_mode)) - if "text_font" in kwargs: - self._text_font = kwargs.pop("text_font") - self.configure(font=self._apply_font_scaling(self._text_font)) + if "font" in kwargs: + self._font = kwargs.pop("font") + super().configure(font=self._apply_font_scaling(self._font)) - super().configure(**kwargs) + if "command" in kwargs: + self._command = kwargs.pop("command") + + if "values" in kwargs: + self._values = kwargs.pop("values") + self._add_menu_commands() + + def cget(self, attribute_name: str) -> any: + if attribute_name == "min_character_width": + return self._min_character_width + + elif attribute_name == "fg_color": + return self._fg_color + elif attribute_name == "hover_color": + return self._hover_color + elif attribute_name == "text_color": + return self._text_color + + elif attribute_name == "font": + return self._font + elif attribute_name == "command": + return self._command + elif attribute_name == "values": + return self._values def _apply_widget_scaling(self, value: Union[int, float, str]) -> Union[float, str]: if isinstance(value, (int, float)): @@ -160,7 +182,7 @@ class DropdownMenu(tkinter.Menu): self._widget_scaling = new_widget_scaling self._spacing_scaling = new_spacing_scaling - self.configure(font=self._apply_font_scaling(self._text_font)) + super().configure(font=self._apply_font_scaling(self._font)) if sys.platform.startswith("win"): self.configure(activeborderwidth=self._apply_widget_scaling(4)) diff --git a/customtkinter/widgets/widget_base_class.py b/customtkinter/widgets/widget_base_class.py index 6b74e00..fafa29a 100644 --- a/customtkinter/widgets/widget_base_class.py +++ b/customtkinter/widgets/widget_base_class.py @@ -15,16 +15,21 @@ from ..appearance_mode_tracker import AppearanceModeTracker from ..scaling_tracker import ScalingTracker from ..theme_manager import ThemeManager +from .widget_helper_functions import filter_dict_by_set + class CTkBaseClass(tkinter.Frame): """ 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 """ - def __init__(self, - *args, - bg_color: Union[str, tuple] = None, + # attributes that are passed to and managed by the tkinter frame only: + _valid_tk_frame_attributes = {"cursor"} + + def __init__(self, *args, width: int, height: int, + + bg_color: Union[str, tuple] = None, **kwargs): super().__init__(*args, width=width, height=height, **kwargs) # set desired size of underlying tkinter.Frame @@ -117,6 +122,10 @@ class CTkBaseClass(tkinter.Frame): return scaled_kwargs + def _draw(self, no_color_updates: bool = False): + """ abstract of draw method to be overridden """ + pass + def config(self, *args, **kwargs): return self.configure(*args, **kwargs) @@ -131,7 +140,7 @@ class CTkBaseClass(tkinter.Frame): self._bg_color = new_bg_color require_redraw = True - super().configure(**kwargs) + super().configure(**filter_dict_by_set(kwargs, self._valid_tk_frame_attributes)) if require_redraw: self._draw() @@ -143,7 +152,8 @@ class CTkBaseClass(tkinter.Frame): return self._desired_width elif key == "height": return self._desired_height - else: + + elif key in self._valid_tk_frame_attributes: return super().cget(key) def _update_dimensions_event(self, event): @@ -241,7 +251,3 @@ class CTkBaseClass(tkinter.Frame): else: return font - - def _draw(self, no_color_updates: bool = False): - """ abstract of draw method to be overridden """ - pass diff --git a/customtkinter/widgets/widget_helper_functions.py b/customtkinter/widgets/widget_helper_functions.py new file mode 100644 index 0000000..85b813e --- /dev/null +++ b/customtkinter/widgets/widget_helper_functions.py @@ -0,0 +1,11 @@ + + +def filter_dict_by_set(dictionary: dict, valid_keys: set): + """ remove all key value pairs, where key is not in valid_keys set """ + new_dictionary = {} + + for key, value in dictionary.items(): + if key in valid_keys: + new_dictionary[key] = value + + return new_dictionary diff --git a/customtkinter/windows/ctk_input_dialog.py b/customtkinter/windows/ctk_input_dialog.py index 5fbda1c..af9f08d 100644 --- a/customtkinter/windows/ctk_input_dialog.py +++ b/customtkinter/windows/ctk_input_dialog.py @@ -18,12 +18,13 @@ class CTkInputDialog: """ def __init__(self, - master: any = None, - title: str = "CTkDialog", - text: str = "CTkDialog", fg_color: Union[str, Tuple[str, str]] = "default_theme", hover_color: Union[str, Tuple[str, str]] = "default_theme", - border_color: Union[str, Tuple[str, str]] = "default_theme"): + border_color: Union[str, Tuple[str, str]] = "default_theme", + + master: any = None, + title: str = "CTkDialog", + text: str = "CTkDialog"): self._appearance_mode = AppearanceModeTracker.get_mode() # 0: "Light" 1: "Dark" self.master = master diff --git a/customtkinter/windows/ctk_tk.py b/customtkinter/windows/ctk_tk.py index 83437b5..0055e97 100644 --- a/customtkinter/windows/ctk_tk.py +++ b/customtkinter/windows/ctk_tk.py @@ -263,6 +263,12 @@ class CTk(tkinter.Tk): super().configure(*args, **kwargs) + def cget(self, attribute_name: str) -> any: + if attribute_name == "fg_color": + return self._fg_color + 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 diff --git a/customtkinter/windows/ctk_toplevel.py b/customtkinter/windows/ctk_toplevel.py index c045ef6..55cc5ef 100644 --- a/customtkinter/windows/ctk_toplevel.py +++ b/customtkinter/windows/ctk_toplevel.py @@ -227,6 +227,12 @@ class CTkToplevel(tkinter.Toplevel): super().configure(*args, **kwargs) + def cget(self, attribute_name: str) -> any: + if attribute_name == "fg_color": + return self._fg_color + 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 diff --git a/examples/complex_example.py b/examples/complex_example.py index 199741a..3baebb6 100644 --- a/examples/complex_example.py +++ b/examples/complex_example.py @@ -42,7 +42,7 @@ class App(customtkinter.CTk): self.label_1 = customtkinter.CTkLabel(master=self.frame_left, text="CustomTkinter", - text_font=("Roboto Medium", -16)) # font name and size in px + font=("Roboto Medium", -16)) # font name and size in px self.label_1.grid(row=1, column=0, pady=10, padx=10) self.button_1 = customtkinter.CTkButton(master=self.frame_left, diff --git a/test/manual_integration_tests/complex_example_new.py b/test/manual_integration_tests/complex_example_new.py index 3838f28..1a9f307 100644 --- a/test/manual_integration_tests/complex_example_new.py +++ b/test/manual_integration_tests/complex_example_new.py @@ -24,7 +24,7 @@ class App(customtkinter.CTk): self.sidebar_frame = customtkinter.CTkFrame(self, width=140) 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", text_font=("Roboto", -16)) + self.logo_label = customtkinter.CTkLabel(self.sidebar_frame, text="CustomTkinter", font=("Roboto", -16)) self.logo_label.grid(row=0, column=0, padx=20, pady=(20, 10)) self.sidebar_button_1 = customtkinter.CTkButton(self.sidebar_frame, command=self.sidebar_button_callback) self.sidebar_button_1.grid(row=1, column=0, padx=20, pady=10) @@ -103,9 +103,9 @@ class App(customtkinter.CTk): self.progressbar_2.grid(row=1, column=0, padx=(20, 10), pady=(10, 10), sticky="ew") self.slider_1 = customtkinter.CTkSlider(self.slider_progressbar_frame, from_=0, to=1, number_of_steps=4) self.slider_1.grid(row=2, column=0, padx=(20, 10), pady=(10, 10), sticky="ew") - self.slider_2 = customtkinter.CTkSlider(self.slider_progressbar_frame, orient="vertical") + self.slider_2 = customtkinter.CTkSlider(self.slider_progressbar_frame, orientation="vertical") self.slider_2.grid(row=0, column=1, rowspan=4, padx=(10, 10), pady=(10, 10), sticky="ns") - self.progressbar_3 = customtkinter.CTkProgressBar(self.slider_progressbar_frame, orient="vertical") + self.progressbar_3 = customtkinter.CTkProgressBar(self.slider_progressbar_frame, orientation="vertical") self.progressbar_3.grid(row=0, column=2, rowspan=4, padx=(10, 20), pady=(10, 10), sticky="ns") # set default values