From a564bc35effb7e9bc55d2b383003199616e802e5 Mon Sep 17 00:00:00 2001 From: Tom Schimansky Date: Tue, 6 Dec 2022 18:09:20 +0100 Subject: [PATCH] fixed progressbar start stop speed increase #775, fixed transparent textbox #779, fixed binding for all widgets #250 #374 #380 #477 #480 --- .../core_widget_classes/ctk_base_class.py | 6 +++ customtkinter/windows/widgets/ctk_button.py | 11 ++-- customtkinter/windows/widgets/ctk_checkbox.py | 19 +++---- customtkinter/windows/widgets/ctk_combobox.py | 42 +++++++++------ customtkinter/windows/widgets/ctk_entry.py | 26 +++++++--- customtkinter/windows/widgets/ctk_frame.py | 13 +++-- customtkinter/windows/widgets/ctk_label.py | 19 ++++--- .../windows/widgets/ctk_optionmenu.py | 52 +++++++++++-------- .../windows/widgets/ctk_progressbar.py | 17 +++--- .../windows/widgets/ctk_radiobutton.py | 41 ++++++++++----- .../windows/widgets/ctk_scrollbar.py | 6 +-- .../windows/widgets/ctk_segmented_button.py | 6 +++ customtkinter/windows/widgets/ctk_slider.py | 33 ++++++++---- customtkinter/windows/widgets/ctk_switch.py | 41 ++++++++++----- customtkinter/windows/widgets/ctk_textbox.py | 33 ++++++------ 15 files changed, 225 insertions(+), 140 deletions(-) diff --git a/customtkinter/windows/widgets/core_widget_classes/ctk_base_class.py b/customtkinter/windows/widgets/core_widget_classes/ctk_base_class.py index a8766ef..500b997 100644 --- a/customtkinter/windows/widgets/core_widget_classes/ctk_base_class.py +++ b/customtkinter/windows/widgets/core_widget_classes/ctk_base_class.py @@ -240,6 +240,12 @@ class CTkBaseClass(tkinter.Frame, CTkAppearanceModeBaseClass, CTkScalingBaseClas super().configure(width=self._apply_widget_scaling(self._desired_width), height=self._apply_widget_scaling(self._desired_height)) + def bind(self, sequence=None, command=None, add=None): + raise NotImplementedError + + def unbind(self, sequence=None, funcid=None): + raise NotImplementedError + def unbind_all(self, sequence): raise AttributeError("'unbind_all' is not allowed, because it would delete necessary internal callbacks for all widgets") diff --git a/customtkinter/windows/widgets/ctk_button.py b/customtkinter/windows/widgets/ctk_button.py index 0c65506..bc3c4fa 100644 --- a/customtkinter/windows/widgets/ctk_button.py +++ b/customtkinter/windows/widgets/ctk_button.py @@ -113,8 +113,6 @@ class CTkButton(CTkBaseClass): self._canvas.bind("", self._on_leave) if sequence is None or sequence == "": self._canvas.bind("", self._clicked) - if sequence is None or sequence == "": - self._canvas.bind("", self._clicked) def _set_scaling(self, *args, **kwargs): super()._set_scaling(*args, **kwargs) @@ -538,15 +536,14 @@ class CTkButton(CTkBaseClass): if self._command is not None: return self._command() - def bind(self, sequence: str = None, command: Callable = None, add: Union[str, bool] = "+") -> str: + def bind(self, sequence: str = None, command: Callable = None, add: Union[str, bool] = True): """ called on the tkinter.Canvas """ if add != "+" or add is not True: raise ValueError("'add' argument can only be '+' or True to preserve internal callbacks") - canvas_bind_return = self._canvas.bind(sequence, command, add="+") - label_bind_return = self._text_label.bind(sequence, command, add="+") - return canvas_bind_return + " + " + label_bind_return + self._canvas.bind(sequence, command, add=True) + self._text_label.bind(sequence, command, add=True) - def unbind(self, sequence: str, funcid: str = None): + def unbind(self, sequence: str = None, funcid: str = None): """ called on the tkinter.Label and tkinter.Canvas """ if funcid is not None: raise ValueError("'funcid' argument can only be None, because there is a bug in" + diff --git a/customtkinter/windows/widgets/ctk_checkbox.py b/customtkinter/windows/widgets/ctk_checkbox.py index 0789786..0b643d6 100644 --- a/customtkinter/windows/widgets/ctk_checkbox.py +++ b/customtkinter/windows/widgets/ctk_checkbox.py @@ -103,10 +103,6 @@ class CTkCheckBox(CTkBaseClass): self._canvas.grid(row=0, column=0, sticky="e") self._draw_engine = DrawEngine(self._canvas) - self._canvas.bind("", self._on_enter) - self._canvas.bind("", self._on_leave) - self._canvas.bind("", self.toggle) - self._text_label = tkinter.Label(master=self, bd=0, padx=0, @@ -131,10 +127,13 @@ class CTkCheckBox(CTkBaseClass): """ set necessary bindings for functionality of widget, will overwrite other bindings """ if sequence is None or sequence == "": self._canvas.bind("", self._on_enter) + self._text_label.bind("", self._on_enter) if sequence is None or sequence == "": self._canvas.bind("", self._on_leave) + self._text_label.bind("", self._on_leave) if sequence is None or sequence == "": self._canvas.bind("", self.toggle) + self._text_label.bind("", self.toggle) def _set_scaling(self, *args, **kwargs): super()._set_scaling(*args, **kwargs) @@ -436,18 +435,20 @@ class CTkCheckBox(CTkBaseClass): def get(self) -> Union[int, str]: return self._onvalue if self._check_state is True else self._offvalue - def bind(self, sequence=None, command=None, add="+"): + def bind(self, sequence: str = None, command: Callable = None, add: Union[str, bool] = True): """ called on the tkinter.Canvas """ if add != "+" or add is not True: raise ValueError("'add' argument can only be '+' or True to preserve internal callbacks") - return self._canvas.bind(sequence, command, add="+") + self._canvas.bind(sequence, command, add=True) + self._text_label.bind(sequence, command, add=True) - def unbind(self, sequence, funcid=None): - """ called on the tkinter.Canvas, restores internal callbacks """ + def unbind(self, sequence: str = None, funcid: str = None): + """ called on the tkinter.Label and tkinter.Canvas """ if funcid is not None: raise ValueError("'funcid' argument can only be None, because there is a bug in" + " tkinter and its not clear whether the internal callbacks will be unbinded or not") - self._canvas.unbind(sequence, None) # unbind all callbacks for sequence + self._canvas.unbind(sequence, None) + self._text_label.unbind(sequence, None) self._create_bindings(sequence=sequence) # restore internal callbacks for sequence def focus(self): diff --git a/customtkinter/windows/widgets/ctk_combobox.py b/customtkinter/windows/widgets/ctk_combobox.py index 070919a..80c84dd 100644 --- a/customtkinter/windows/widgets/ctk_combobox.py +++ b/customtkinter/windows/widgets/ctk_combobox.py @@ -102,6 +102,11 @@ class CTkComboBox(CTkBaseClass): font=self._apply_font_scaling(self._font)) self._create_grid() + self._create_bindings() + self._draw() # initial draw + + if self._variable is not None: + self._entry.configure(textvariable=self._variable) # insert default value if len(self._values) > 0: @@ -109,18 +114,15 @@ class CTkComboBox(CTkBaseClass): else: self._entry.insert(0, "CTkComboBox") - self._draw() # initial draw - - # event bindings - self._canvas.tag_bind("right_parts", "", self._on_enter) - self._canvas.tag_bind("dropdown_arrow", "", self._on_enter) - self._canvas.tag_bind("right_parts", "", self._on_leave) - self._canvas.tag_bind("dropdown_arrow", "", self._on_leave) - self._canvas.tag_bind("right_parts", "", self._clicked) - self._canvas.tag_bind("dropdown_arrow", "", self._clicked) - - if self._variable is not None: - self._entry.configure(textvariable=self._variable) + def _create_bindings(self, sequence: Optional[str] = None): + """ set necessary bindings for functionality of widget, will overwrite other bindings """ + if sequence is None: + self._canvas.tag_bind("right_parts", "", self._on_enter) + self._canvas.tag_bind("dropdown_arrow", "", self._on_enter) + self._canvas.tag_bind("right_parts", "", self._on_leave) + self._canvas.tag_bind("dropdown_arrow", "", self._on_leave) + self._canvas.tag_bind("right_parts", "", self._clicked) + self._canvas.tag_bind("dropdown_arrow", "", self._clicked) def _create_grid(self): self._canvas.grid(row=0, column=0, rowspan=1, columnspan=1, sticky="nsew") @@ -391,17 +393,23 @@ class CTkComboBox(CTkBaseClass): def get(self) -> str: return self._entry.get() - def _clicked(self, event=0): + def _clicked(self, event=None): if self._state is not tkinter.DISABLED and len(self._values) > 0: self._open_dropdown_menu() - def bind(self, sequence=None, command=None, add=None): + def bind(self, sequence=None, command=None, add=True): """ called on the tkinter.Entry """ - return self._entry.bind(sequence, command, add) + if add != "+" or add is not True: + raise ValueError("'add' argument can only be '+' or True to preserve internal callbacks") + self._entry.bind(sequence, command, add=True) - def unbind(self, sequence, funcid=None): + def unbind(self, sequence=None, funcid=None): """ called on the tkinter.Entry """ - return self._entry.unbind(sequence, funcid) + if funcid is not None: + raise ValueError("'funcid' argument can only be None, because there is a bug in" + + " tkinter and its not clear whether the internal callbacks will be unbinded or not") + self._entry.unbind(sequence, None) # unbind all callbacks for sequence + self._create_bindings(sequence=sequence) # restore internal callbacks for sequence def focus(self): return self._entry.focus() diff --git a/customtkinter/windows/widgets/ctk_entry.py b/customtkinter/windows/widgets/ctk_entry.py index ac1edb6..d6e8ab2 100644 --- a/customtkinter/windows/widgets/ctk_entry.py +++ b/customtkinter/windows/widgets/ctk_entry.py @@ -90,16 +90,20 @@ class CTkEntry(CTkBaseClass): textvariable=self._textvariable, **pop_from_dict_by_set(kwargs, self._valid_tk_entry_attributes)) - self._create_grid() - check_kwargs_empty(kwargs, raise_error=True) - self._entry.bind('', self._entry_focus_out) - self._entry.bind('', self._entry_focus_in) - + self._create_grid() self._activate_placeholder() + self._create_bindings() self._draw() + def _create_bindings(self, sequence: Optional[str] = None): + """ set necessary bindings for functionality of widget, will overwrite other bindings """ + if sequence is None or sequence == "": + self._entry.bind("", self._entry_focus_in) + if sequence is None or sequence == "": + self._entry.bind("", self._entry_focus_out) + def _create_grid(self): self._canvas.grid(column=0, row=0, sticky="nswe") @@ -275,13 +279,19 @@ class CTkEntry(CTkBaseClass): else: return super().cget(attribute_name) # cget of CTkBaseClass - def bind(self, sequence=None, command=None, add=None): + def bind(self, sequence=None, command=None, add=True): """ called on the tkinter.Entry """ - return self._entry.bind(sequence, command, add) + if add != "+" or add is not True: + raise ValueError("'add' argument can only be '+' or True to preserve internal callbacks") + self._entry.bind(sequence, command, add=True) def unbind(self, sequence, funcid=None): """ called on the tkinter.Entry """ - return self._entry.unbind(sequence, funcid) + if funcid is not None: + raise ValueError("'funcid' argument can only be None, because there is a bug in" + + " tkinter and its not clear whether the internal callbacks will be unbinded or not") + self._entry.unbind(sequence, None) # unbind all callbacks for sequence + self._create_bindings(sequence=sequence) # restore internal callbacks for sequence def _activate_placeholder(self): if self._entry.get() == "" and self._placeholder_text is not None and (self._textvariable is None or self._textvariable == ""): diff --git a/customtkinter/windows/widgets/ctk_frame.py b/customtkinter/windows/widgets/ctk_frame.py index baa9fc3..fe9e226 100644 --- a/customtkinter/windows/widgets/ctk_frame.py +++ b/customtkinter/windows/widgets/ctk_frame.py @@ -182,10 +182,15 @@ class CTkFrame(CTkBaseClass): else: return super().cget(attribute_name) - def bind(self, sequence=None, command=None, add=None): + def bind(self, sequence=None, command=None, add=True): """ called on the tkinter.Canvas """ - return self._canvas.bind(sequence, command, add) + if not (add == "+" or add is True): + raise ValueError("'add' argument can only be '+' or True to preserve internal callbacks") + self._canvas.bind(sequence, command, add=True) - def unbind(self, sequence, funcid=None): + def unbind(self, sequence=None, funcid=None): """ called on the tkinter.Canvas """ - return self._canvas.unbind(sequence, funcid) + if funcid is not None: + raise ValueError("'funcid' argument can only be None, because there is a bug in" + + " tkinter and its not clear whether the internal callbacks will be unbinded or not") + self._canvas.unbind(sequence, None) diff --git a/customtkinter/windows/widgets/ctk_label.py b/customtkinter/windows/widgets/ctk_label.py index 293f923..490fdd0 100644 --- a/customtkinter/windows/widgets/ctk_label.py +++ b/customtkinter/windows/widgets/ctk_label.py @@ -247,17 +247,20 @@ class CTkLabel(CTkBaseClass): else: return super().cget(attribute_name) # cget of CTkBaseClass - def bind(self, sequence: str = None, command: Callable = None, add: str = None) -> str: + def bind(self, sequence: str = None, command: Callable = None, add: str = True): """ called on the tkinter.Label and tkinter.Canvas """ - canvas_bind_return = self._canvas.bind(sequence, command, add) - label_bind_return = self._label.bind(sequence, command, add) - return canvas_bind_return + " + " + label_bind_return + if add != "+" or add is not True: + raise ValueError("'add' argument can only be '+' or True to preserve internal callbacks") + self._canvas.bind(sequence, command, add=True) + self._label.bind(sequence, command, add=True) - def unbind(self, sequence: str, funcid: str = None): + def unbind(self, sequence: str, funcid: Optional[str] = None): """ called on the tkinter.Label and tkinter.Canvas """ - canvas_bind_return, label_bind_return = funcid.split(" + ") - self._canvas.unbind(sequence, canvas_bind_return) - self._label.unbind(sequence, label_bind_return) + if funcid is not None: + raise ValueError("'funcid' argument can only be None, because there is a bug in" + + " tkinter and its not clear whether the internal callbacks will be unbinded or not") + self._canvas.unbind(sequence, None) + self._label.unbind(sequence, None) def focus(self): return self._label.focus() diff --git a/customtkinter/windows/widgets/ctk_optionmenu.py b/customtkinter/windows/widgets/ctk_optionmenu.py index e6229fe..64f661c 100644 --- a/customtkinter/windows/widgets/ctk_optionmenu.py +++ b/customtkinter/windows/widgets/ctk_optionmenu.py @@ -107,10 +107,6 @@ class CTkOptionMenu(CTkBaseClass): pady=0, borderwidth=1, text=self._current_value) - self._create_grid() - - if not self._dynamic_resizing: - self.grid_propagate(0) if self._cursor_manipulation_enabled: if sys.platform == "darwin": @@ -118,17 +114,11 @@ class CTkOptionMenu(CTkBaseClass): elif sys.platform.startswith("win"): self.configure(cursor="hand2") - # event bindings - self._canvas.bind("", self._on_enter) - self._canvas.bind("", self._on_leave) - self._canvas.bind("", self._clicked) - self._canvas.bind("", self._clicked) - - self._text_label.bind("", self._on_enter) - self._text_label.bind("", self._on_leave) - self._text_label.bind("", self._clicked) - self._text_label.bind("", self._clicked) + self._create_grid() + if not self._dynamic_resizing: + self.grid_propagate(0) + self._create_bindings() self._draw() # initial draw if self._variable is not None: @@ -136,6 +126,18 @@ class CTkOptionMenu(CTkBaseClass): self._current_value = self._variable.get() self._text_label.configure(text=self._current_value) + def _create_bindings(self, sequence: Optional[str] = None): + """ set necessary bindings for functionality of widget, will overwrite other bindings """ + if sequence is None or sequence == "": + self._canvas.bind("", self._on_enter) + self._text_label.bind("", self._on_enter) + if sequence is None or sequence == "": + self._canvas.bind("", self._on_leave) + self._text_label.bind("", self._on_leave) + if sequence is None or sequence == "": + self._canvas.bind("", self._clicked) + self._text_label.bind("", self._clicked) + def _create_grid(self): self._canvas.grid(row=0, column=0, sticky="nsew") @@ -393,17 +395,21 @@ class CTkOptionMenu(CTkBaseClass): if self._state is not tkinter.DISABLED and len(self._values) > 0: self._open_dropdown_menu() - def bind(self, sequence: str = None, command: Callable = None, add: str = None) -> str: - """ called on the tkinter.Label and tkinter.Canvas """ - canvas_bind_return = self._canvas.bind(sequence, command, add) - label_bind_return = self._text_label.bind(sequence, command, add) - return canvas_bind_return + " + " + label_bind_return + def bind(self, sequence: str = None, command: Callable = None, add: Union[str, bool] = True): + """ called on the tkinter.Canvas """ + if add != "+" or add is not True: + raise ValueError("'add' argument can only be '+' or True to preserve internal callbacks") + self._canvas.bind(sequence, command, add=True) + self._text_label.bind(sequence, command, add=True) - def unbind(self, sequence: str, funcid: str = None): + def unbind(self, sequence: str = None, funcid: str = None): """ called on the tkinter.Label and tkinter.Canvas """ - canvas_bind_return, label_bind_return = funcid.split(" + ") - self._canvas.unbind(sequence, canvas_bind_return) - self._text_label.unbind(sequence, label_bind_return) + if funcid is not None: + raise ValueError("'funcid' argument can only be None, because there is a bug in" + + " tkinter and its not clear whether the internal callbacks will be unbinded or not") + self._canvas.unbind(sequence, None) + self._text_label.unbind(sequence, None) + self._create_bindings(sequence=sequence) # restore internal callbacks for sequence def focus(self): return self._text_label.focus() diff --git a/customtkinter/windows/widgets/ctk_progressbar.py b/customtkinter/windows/widgets/ctk_progressbar.py index c6285ae..ce597af 100644 --- a/customtkinter/windows/widgets/ctk_progressbar.py +++ b/customtkinter/windows/widgets/ctk_progressbar.py @@ -1,6 +1,6 @@ import tkinter import math -from typing import Union, Tuple, Optional, Literal +from typing import Union, Tuple, Optional, Literal, Callable from .core_rendering import CTkCanvas from .theme import ThemeManager @@ -285,13 +285,18 @@ class CTkProgressBar(CTkBaseClass): self._indeterminate_value += self._indeterminate_speed self._draw() - def bind(self, sequence=None, command=None, add=None): + def bind(self, sequence: str = None, command: Callable = None, add: Union[str, bool] = True): """ called on the tkinter.Canvas """ - return self._canvas.bind(sequence, command, add) + if add != "+" or add is not True: + raise ValueError("'add' argument can only be '+' or True to preserve internal callbacks") + self._canvas.bind(sequence, command, add=True) - def unbind(self, sequence, funcid=None): - """ called on the tkinter.Canvas """ - return self._canvas.unbind(sequence, funcid) + def unbind(self, sequence: str = None, funcid: str = None): + """ called on the tkinter.Label and tkinter.Canvas """ + if funcid is not None: + raise ValueError("'funcid' argument can only be None, because there is a bug in" + + " tkinter and its not clear whether the internal callbacks will be unbinded or not") + self._canvas.unbind(sequence, None) def focus(self): return self._canvas.focus() diff --git a/customtkinter/windows/widgets/ctk_radiobutton.py b/customtkinter/windows/widgets/ctk_radiobutton.py index 7fcfe62..4fc652d 100644 --- a/customtkinter/windows/widgets/ctk_radiobutton.py +++ b/customtkinter/windows/widgets/ctk_radiobutton.py @@ -99,10 +99,6 @@ class CTkRadioButton(CTkBaseClass): self._canvas.grid(row=0, column=0) self._draw_engine = DrawEngine(self._canvas) - self._canvas.bind("", self._on_enter) - self._canvas.bind("", self._on_leave) - self._canvas.bind("", self.invoke) - self._text_label = tkinter.Label(master=self, bd=0, padx=0, @@ -114,16 +110,25 @@ class CTkRadioButton(CTkBaseClass): self._text_label.grid(row=0, column=2, sticky="w") self._text_label["anchor"] = "w" - self._text_label.bind("", self._on_enter) - self._text_label.bind("", self._on_leave) - self._text_label.bind("", self.invoke) - if self._variable is not None: self._variable_callback_name = self._variable.trace_add("write", self._variable_callback) self._check_state = True if self._variable.get() == self._value else False - self._draw() # initial draw + self._create_bindings() self._set_cursor() + self._draw() + + def _create_bindings(self, sequence: Optional[str] = None): + """ set necessary bindings for functionality of widget, will overwrite other bindings """ + if sequence is None or sequence == "": + self._canvas.bind("", self._on_enter) + self._text_label.bind("", self._on_enter) + if sequence is None or sequence == "": + self._canvas.bind("", self._on_leave) + self._text_label.bind("", self._on_leave) + if sequence is None or sequence == "": + self._canvas.bind("", self.invoke) + self._text_label.bind("", self.invoke) def _set_scaling(self, *args, **kwargs): super()._set_scaling(*args, **kwargs) @@ -398,13 +403,21 @@ class CTkRadioButton(CTkBaseClass): self._variable.set("") self._variable_callback_blocked = False - def bind(self, sequence=None, command=None, add=None): + def bind(self, sequence: str = None, command: Callable = None, add: Union[str, bool] = True): """ called on the tkinter.Canvas """ - return self._canvas.bind(sequence, command, add) + if add != "+" or add is not True: + raise ValueError("'add' argument can only be '+' or True to preserve internal callbacks") + self._canvas.bind(sequence, command, add=True) + self._text_label.bind(sequence, command, add=True) - def unbind(self, sequence, funcid=None): - """ called on the tkinter.Canvas """ - return self._canvas.unbind(sequence, funcid) + def unbind(self, sequence: str = None, funcid: str = None): + """ called on the tkinter.Label and tkinter.Canvas """ + if funcid is not None: + raise ValueError("'funcid' argument can only be None, because there is a bug in" + + " tkinter and its not clear whether the internal callbacks will be unbinded or not") + self._canvas.unbind(sequence, None) + self._text_label.unbind(sequence, None) + self._create_bindings(sequence=sequence) # restore internal callbacks for sequence def focus(self): return self._text_label.focus() diff --git a/customtkinter/windows/widgets/ctk_scrollbar.py b/customtkinter/windows/widgets/ctk_scrollbar.py index b53e63b..1c03095 100644 --- a/customtkinter/windows/widgets/ctk_scrollbar.py +++ b/customtkinter/windows/widgets/ctk_scrollbar.py @@ -257,13 +257,13 @@ class CTkScrollbar(CTkBaseClass): def get(self): return self._start_value, self._end_value - def bind(self, sequence=None, command=None, add="+"): + def bind(self, sequence=None, command=None, add=True): """ called on the tkinter.Canvas """ if add != "+" or add is not True: raise ValueError("'add' argument can only be '+' or True to preserve internal callbacks") - return self._canvas.bind(sequence, command, add="+") + self._canvas.bind(sequence, command, add=True) - def unbind(self, sequence, funcid=None): + def unbind(self, sequence=None, funcid=None): """ called on the tkinter.Canvas, restores internal callbacks """ if funcid is not None: raise ValueError("'funcid' argument can only be None, because there is a bug in" + diff --git a/customtkinter/windows/widgets/ctk_segmented_button.py b/customtkinter/windows/widgets/ctk_segmented_button.py index 05b57c9..af7c19a 100644 --- a/customtkinter/windows/widgets/ctk_segmented_button.py +++ b/customtkinter/windows/widgets/ctk_segmented_button.py @@ -408,3 +408,9 @@ class CTkSegmentedButton(CTkFrame): else: raise ValueError(f"CTkSegmentedButton does not contain value '{value}'") + def bind(self, sequence=None, command=None, add=None): + raise NotImplementedError + + def unbind(self, sequence=None, funcid=None): + raise NotImplementedError + diff --git a/customtkinter/windows/widgets/ctk_slider.py b/customtkinter/windows/widgets/ctk_slider.py index a88e956..04e312c 100644 --- a/customtkinter/windows/widgets/ctk_slider.py +++ b/customtkinter/windows/widgets/ctk_slider.py @@ -96,11 +96,7 @@ class CTkSlider(CTkBaseClass): self._canvas.grid(column=0, row=0, rowspan=1, columnspan=1, sticky="nswe") self._draw_engine = DrawEngine(self._canvas) - self._canvas.bind("", self._on_enter) - self._canvas.bind("", self._on_leave) - self._canvas.bind("", self._clicked) - self._canvas.bind("", self._clicked) - + self._create_bindings() self._set_cursor() self._draw() # initial draw @@ -110,6 +106,17 @@ class CTkSlider(CTkBaseClass): self.set(self._variable.get(), from_variable_callback=True) self._variable_callback_blocked = False + def _create_bindings(self, sequence: Optional[str] = None): + """ set necessary bindings for functionality of widget, will overwrite other bindings """ + if sequence is None or sequence == "": + self._canvas.bind("", self._on_enter) + if sequence is None or sequence == "": + self._canvas.bind("", self._on_leave) + if sequence is None or sequence == "": + self._canvas.bind("", self._clicked) + if sequence is None or sequence == "": + self._canvas.bind("", self._clicked) + def _set_scaling(self, *args, **kwargs): super()._set_scaling(*args, **kwargs) @@ -366,13 +373,19 @@ class CTkSlider(CTkBaseClass): if not self._variable_callback_blocked: self.set(self._variable.get(), from_variable_callback=True) - def bind(self, sequence=None, command=None, add=None): + def bind(self, sequence: str = None, command: Callable = None, add: Union[str, bool] = True): """ called on the tkinter.Canvas """ - return self._canvas.bind(sequence, command, add) + if add != "+" or add is not True: + raise ValueError("'add' argument can only be '+' or True to preserve internal callbacks") + self._canvas.bind(sequence, command, add=True) - def unbind(self, sequence, funcid=None): - """ called on the tkinter.Canvas """ - return self._canvas.unbind(sequence, funcid) + def unbind(self, sequence: str = None, funcid: str = None): + """ called on the tkinter.Label and tkinter.Canvas """ + if funcid is not None: + raise ValueError("'funcid' argument can only be None, because there is a bug in" + + " tkinter and its not clear whether the internal callbacks will be unbinded or not") + self._canvas.unbind(sequence, None) + self._create_bindings(sequence=sequence) # restore internal callbacks for sequence def focus(self): return self._canvas.focus() diff --git a/customtkinter/windows/widgets/ctk_switch.py b/customtkinter/windows/widgets/ctk_switch.py index a986a65..0b6b8ef 100644 --- a/customtkinter/windows/widgets/ctk_switch.py +++ b/customtkinter/windows/widgets/ctk_switch.py @@ -106,10 +106,6 @@ class CTkSwitch(CTkBaseClass): self._canvas.grid(row=0, column=0, sticky="") self._draw_engine = DrawEngine(self._canvas) - self._canvas.bind("", self._on_enter) - self._canvas.bind("", self._on_leave) - self._canvas.bind("", self.toggle) - self._text_label = tkinter.Label(master=self, bd=0, padx=0, @@ -121,16 +117,25 @@ class CTkSwitch(CTkBaseClass): self._text_label.grid(row=0, column=2, sticky="w") self._text_label["anchor"] = "w" - self._text_label.bind("", self._on_enter) - self._text_label.bind("", self._on_leave) - self._text_label.bind("", self.toggle) - if self._variable is not None and self._variable != "": self._variable_callback_name = self._variable.trace_add("write", self._variable_callback) self.c_heck_state = True if self._variable.get() == self._onvalue else False - self._draw() # initial draw + self._create_bindings() self._set_cursor() + self._draw() # initial draw + + def _create_bindings(self, sequence: Optional[str] = None): + """ set necessary bindings for functionality of widget, will overwrite other bindings """ + if sequence is None or sequence == "": + self._canvas.bind("", self._on_enter) + self._text_label.bind("", self._on_enter) + if sequence is None or sequence == "": + self._canvas.bind("", self._on_leave) + self._text_label.bind("", self._on_leave) + if sequence is None or sequence == "": + self._canvas.bind("", self.toggle) + self._text_label.bind("", self.toggle) def _set_scaling(self, *args, **kwargs): super()._set_scaling(*args, **kwargs) @@ -437,13 +442,21 @@ class CTkSwitch(CTkBaseClass): elif self._variable.get() == self._offvalue: self.deselect(from_variable_callback=True) - def bind(self, sequence=None, command=None, add=None): + def bind(self, sequence: str = None, command: Callable = None, add: Union[str, bool] = True): """ called on the tkinter.Canvas """ - return self._canvas.bind(sequence, command, add) + if add != "+" or add is not True: + raise ValueError("'add' argument can only be '+' or True to preserve internal callbacks") + self._canvas.bind(sequence, command, add=True) + self._text_label.bind(sequence, command, add=True) - def unbind(self, sequence, funcid=None): - """ called on the tkinter.Canvas """ - return self._canvas.unbind(sequence, funcid) + def unbind(self, sequence: str = None, funcid: str = None): + """ called on the tkinter.Label and tkinter.Canvas """ + if funcid is not None: + raise ValueError("'funcid' argument can only be None, because there is a bug in" + + " tkinter and its not clear whether the internal callbacks will be unbinded or not") + self._canvas.unbind(sequence, None) + self._text_label.unbind(sequence, None) + self._create_bindings(sequence=sequence) # restore internal callbacks for sequence def focus(self): return self._text_label.focus() diff --git a/customtkinter/windows/widgets/ctk_textbox.py b/customtkinter/windows/widgets/ctk_textbox.py index 2ba985a..7d1ad9d 100644 --- a/customtkinter/windows/widgets/ctk_textbox.py +++ b/customtkinter/windows/widgets/ctk_textbox.py @@ -1,5 +1,5 @@ import tkinter -from typing import Union, Tuple, Optional +from typing import Union, Tuple, Optional, Callable from .core_rendering import CTkCanvas from .ctk_scrollbar import CTkScrollbar @@ -86,7 +86,6 @@ class CTkTextbox(CTkBaseClass): highlightthickness=0, relief="flat", insertbackground=self._apply_appearance_mode(self._text_color), - bg=self._apply_appearance_mode(self._fg_color), **pop_from_dict_by_set(kwargs, self._valid_tk_text_attributes)) check_kwargs_empty(kwargs, raise_error=True) @@ -227,10 +226,10 @@ class CTkTextbox(CTkBaseClass): self._textbox.configure(fg=self._apply_appearance_mode(self._text_color), bg=self._apply_appearance_mode(self._bg_color), insertbackground=self._apply_appearance_mode(self._text_color)) - self._x_scrollbar.configure(fg_color=self._bg_color, scrollbar_color=self._scrollbar_button_color, - scrollbar_hover_color=self._scrollbar_button_hover_color) - self._y_scrollbar.configure(fg_color=self._bg_color, scrollbar_color=self._scrollbar_button_color, - scrollbar_hover_color=self._scrollbar_button_hover_color) + self._x_scrollbar.configure(fg_color=self._bg_color, button_color=self._scrollbar_button_color, + button_hover_color=self._scrollbar_button_hover_color) + self._y_scrollbar.configure(fg_color=self._bg_color, button_color=self._scrollbar_button_color, + button_hover_color=self._scrollbar_button_hover_color) else: self._canvas.itemconfig("inner_parts", fill=self._apply_appearance_mode(self._fg_color), @@ -327,18 +326,18 @@ class CTkTextbox(CTkBaseClass): else: return super().cget(attribute_name) - def bind(self, sequence=None, command=None, add=None): - """ called on the tkinter.Text """ + def bind(self, sequence: str = None, command: Callable = None, add: Union[str, bool] = True): + """ called on the tkinter.Canvas """ + if add != "+" or add is not True: + raise ValueError("'add' argument can only be '+' or True to preserve internal callbacks") + self._textbox.bind(sequence, command, add=True) - # if sequence is , allow only to add the binding to keep the _textbox_modified_event() being called - if sequence == "": - return self._textbox.bind(sequence, command, add="+") - else: - return self._textbox.bind(sequence, command, add) - - def unbind(self, sequence, funcid=None): - """ called on the tkinter.Text """ - return self._textbox.unbind(sequence, funcid) + def unbind(self, sequence: str = None, funcid: str = None): + """ called on the tkinter.Label and tkinter.Canvas """ + if funcid is not None: + raise ValueError("'funcid' argument can only be None, because there is a bug in" + + " tkinter and its not clear whether the internal callbacks will be unbinded or not") + self._textbox.unbind(sequence, None) def focus(self): return self._textbox.focus()