fixed progressbar start stop speed increase #775, fixed transparent textbox #779, fixed binding for all widgets #250 #374 #380 #477 #480

This commit is contained in:
Tom Schimansky 2022-12-06 18:09:20 +01:00
parent dd223a15b5
commit a564bc35ef
15 changed files with 225 additions and 140 deletions

View File

@ -240,6 +240,12 @@ class CTkBaseClass(tkinter.Frame, CTkAppearanceModeBaseClass, CTkScalingBaseClas
super().configure(width=self._apply_widget_scaling(self._desired_width), super().configure(width=self._apply_widget_scaling(self._desired_width),
height=self._apply_widget_scaling(self._desired_height)) 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): def unbind_all(self, sequence):
raise AttributeError("'unbind_all' is not allowed, because it would delete necessary internal callbacks for all widgets") raise AttributeError("'unbind_all' is not allowed, because it would delete necessary internal callbacks for all widgets")

View File

@ -113,8 +113,6 @@ class CTkButton(CTkBaseClass):
self._canvas.bind("<Leave>", self._on_leave) self._canvas.bind("<Leave>", self._on_leave)
if sequence is None or sequence == "<Button-1>": if sequence is None or sequence == "<Button-1>":
self._canvas.bind("<Button-1>", self._clicked) self._canvas.bind("<Button-1>", self._clicked)
if sequence is None or sequence == "<Button-1>":
self._canvas.bind("<Button-1>", self._clicked)
def _set_scaling(self, *args, **kwargs): def _set_scaling(self, *args, **kwargs):
super()._set_scaling(*args, **kwargs) super()._set_scaling(*args, **kwargs)
@ -538,15 +536,14 @@ class CTkButton(CTkBaseClass):
if self._command is not None: if self._command is not None:
return self._command() 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 """ """ called on the tkinter.Canvas """
if add != "+" or add is not True: if add != "+" or add is not True:
raise ValueError("'add' argument can only be '+' or True to preserve internal callbacks") raise ValueError("'add' argument can only be '+' or True to preserve internal callbacks")
canvas_bind_return = self._canvas.bind(sequence, command, add="+") self._canvas.bind(sequence, command, add=True)
label_bind_return = self._text_label.bind(sequence, command, add="+") self._text_label.bind(sequence, command, add=True)
return canvas_bind_return + " + " + label_bind_return
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 """ """ called on the tkinter.Label and tkinter.Canvas """
if funcid is not None: if funcid is not None:
raise ValueError("'funcid' argument can only be None, because there is a bug in" + raise ValueError("'funcid' argument can only be None, because there is a bug in" +

View File

@ -103,10 +103,6 @@ class CTkCheckBox(CTkBaseClass):
self._canvas.grid(row=0, column=0, sticky="e") self._canvas.grid(row=0, column=0, sticky="e")
self._draw_engine = DrawEngine(self._canvas) self._draw_engine = DrawEngine(self._canvas)
self._canvas.bind("<Enter>", self._on_enter)
self._canvas.bind("<Leave>", self._on_leave)
self._canvas.bind("<Button-1>", self.toggle)
self._text_label = tkinter.Label(master=self, self._text_label = tkinter.Label(master=self,
bd=0, bd=0,
padx=0, padx=0,
@ -131,10 +127,13 @@ class CTkCheckBox(CTkBaseClass):
""" set necessary bindings for functionality of widget, will overwrite other bindings """ """ set necessary bindings for functionality of widget, will overwrite other bindings """
if sequence is None or sequence == "<Enter>": if sequence is None or sequence == "<Enter>":
self._canvas.bind("<Enter>", self._on_enter) self._canvas.bind("<Enter>", self._on_enter)
self._text_label.bind("<Enter>", self._on_enter)
if sequence is None or sequence == "<Leave>": if sequence is None or sequence == "<Leave>":
self._canvas.bind("<Leave>", self._on_leave) self._canvas.bind("<Leave>", self._on_leave)
self._text_label.bind("<Leave>", self._on_leave)
if sequence is None or sequence == "<Button-1>": if sequence is None or sequence == "<Button-1>":
self._canvas.bind("<Button-1>", self.toggle) self._canvas.bind("<Button-1>", self.toggle)
self._text_label.bind("<Button-1>", self.toggle)
def _set_scaling(self, *args, **kwargs): def _set_scaling(self, *args, **kwargs):
super()._set_scaling(*args, **kwargs) super()._set_scaling(*args, **kwargs)
@ -436,18 +435,20 @@ class CTkCheckBox(CTkBaseClass):
def get(self) -> Union[int, str]: def get(self) -> Union[int, str]:
return self._onvalue if self._check_state is True else self._offvalue 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 """ """ called on the tkinter.Canvas """
if add != "+" or add is not True: if add != "+" or add is not True:
raise ValueError("'add' argument can only be '+' or True to preserve internal callbacks") 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): def unbind(self, sequence: str = None, funcid: str = None):
""" called on the tkinter.Canvas, restores internal callbacks """ """ called on the tkinter.Label and tkinter.Canvas """
if funcid is not None: if funcid is not None:
raise ValueError("'funcid' argument can only be None, because there is a bug in" + 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") " 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 self._create_bindings(sequence=sequence) # restore internal callbacks for sequence
def focus(self): def focus(self):

View File

@ -102,6 +102,11 @@ class CTkComboBox(CTkBaseClass):
font=self._apply_font_scaling(self._font)) font=self._apply_font_scaling(self._font))
self._create_grid() 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 # insert default value
if len(self._values) > 0: if len(self._values) > 0:
@ -109,18 +114,15 @@ class CTkComboBox(CTkBaseClass):
else: else:
self._entry.insert(0, "CTkComboBox") self._entry.insert(0, "CTkComboBox")
self._draw() # initial draw def _create_bindings(self, sequence: Optional[str] = None):
""" set necessary bindings for functionality of widget, will overwrite other bindings """
# event bindings if sequence is None:
self._canvas.tag_bind("right_parts", "<Enter>", self._on_enter) self._canvas.tag_bind("right_parts", "<Enter>", self._on_enter)
self._canvas.tag_bind("dropdown_arrow", "<Enter>", self._on_enter) self._canvas.tag_bind("dropdown_arrow", "<Enter>", self._on_enter)
self._canvas.tag_bind("right_parts", "<Leave>", self._on_leave) self._canvas.tag_bind("right_parts", "<Leave>", self._on_leave)
self._canvas.tag_bind("dropdown_arrow", "<Leave>", self._on_leave) self._canvas.tag_bind("dropdown_arrow", "<Leave>", self._on_leave)
self._canvas.tag_bind("right_parts", "<Button-1>", self._clicked) self._canvas.tag_bind("right_parts", "<Button-1>", self._clicked)
self._canvas.tag_bind("dropdown_arrow", "<Button-1>", self._clicked) self._canvas.tag_bind("dropdown_arrow", "<Button-1>", self._clicked)
if self._variable is not None:
self._entry.configure(textvariable=self._variable)
def _create_grid(self): def _create_grid(self):
self._canvas.grid(row=0, column=0, rowspan=1, columnspan=1, sticky="nsew") self._canvas.grid(row=0, column=0, rowspan=1, columnspan=1, sticky="nsew")
@ -391,17 +393,23 @@ class CTkComboBox(CTkBaseClass):
def get(self) -> str: def get(self) -> str:
return self._entry.get() 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: if self._state is not tkinter.DISABLED and len(self._values) > 0:
self._open_dropdown_menu() 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 """ """ 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 """ """ 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): def focus(self):
return self._entry.focus() return self._entry.focus()

View File

@ -90,16 +90,20 @@ class CTkEntry(CTkBaseClass):
textvariable=self._textvariable, textvariable=self._textvariable,
**pop_from_dict_by_set(kwargs, self._valid_tk_entry_attributes)) **pop_from_dict_by_set(kwargs, self._valid_tk_entry_attributes))
self._create_grid()
check_kwargs_empty(kwargs, raise_error=True) check_kwargs_empty(kwargs, raise_error=True)
self._entry.bind('<FocusOut>', self._entry_focus_out) self._create_grid()
self._entry.bind('<FocusIn>', self._entry_focus_in)
self._activate_placeholder() self._activate_placeholder()
self._create_bindings()
self._draw() 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 == "<FocusIn>":
self._entry.bind("<FocusIn>", self._entry_focus_in)
if sequence is None or sequence == "<FocusOut>":
self._entry.bind("<FocusOut>", self._entry_focus_out)
def _create_grid(self): def _create_grid(self):
self._canvas.grid(column=0, row=0, sticky="nswe") self._canvas.grid(column=0, row=0, sticky="nswe")
@ -275,13 +279,19 @@ class CTkEntry(CTkBaseClass):
else: else:
return super().cget(attribute_name) # cget of CTkBaseClass 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 """ """ 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, funcid=None):
""" called on the tkinter.Entry """ """ 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): def _activate_placeholder(self):
if self._entry.get() == "" and self._placeholder_text is not None and (self._textvariable is None or self._textvariable == ""): if self._entry.get() == "" and self._placeholder_text is not None and (self._textvariable is None or self._textvariable == ""):

View File

@ -182,10 +182,15 @@ class CTkFrame(CTkBaseClass):
else: else:
return super().cget(attribute_name) 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 """ """ 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 """ """ 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)

View File

@ -247,17 +247,20 @@ class CTkLabel(CTkBaseClass):
else: else:
return super().cget(attribute_name) # cget of CTkBaseClass 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 """ """ called on the tkinter.Label and tkinter.Canvas """
canvas_bind_return = self._canvas.bind(sequence, command, add) if add != "+" or add is not True:
label_bind_return = self._label.bind(sequence, command, add) raise ValueError("'add' argument can only be '+' or True to preserve internal callbacks")
return canvas_bind_return + " + " + label_bind_return 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 """ """ called on the tkinter.Label and tkinter.Canvas """
canvas_bind_return, label_bind_return = funcid.split(" + ") if funcid is not None:
self._canvas.unbind(sequence, canvas_bind_return) raise ValueError("'funcid' argument can only be None, because there is a bug in" +
self._label.unbind(sequence, label_bind_return) " 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): def focus(self):
return self._label.focus() return self._label.focus()

View File

@ -107,10 +107,6 @@ class CTkOptionMenu(CTkBaseClass):
pady=0, pady=0,
borderwidth=1, borderwidth=1,
text=self._current_value) text=self._current_value)
self._create_grid()
if not self._dynamic_resizing:
self.grid_propagate(0)
if self._cursor_manipulation_enabled: if self._cursor_manipulation_enabled:
if sys.platform == "darwin": if sys.platform == "darwin":
@ -118,17 +114,11 @@ class CTkOptionMenu(CTkBaseClass):
elif sys.platform.startswith("win"): elif sys.platform.startswith("win"):
self.configure(cursor="hand2") self.configure(cursor="hand2")
# event bindings self._create_grid()
self._canvas.bind("<Enter>", self._on_enter) if not self._dynamic_resizing:
self._canvas.bind("<Leave>", self._on_leave) self.grid_propagate(0)
self._canvas.bind("<Button-1>", self._clicked)
self._canvas.bind("<Button-1>", self._clicked)
self._text_label.bind("<Enter>", self._on_enter)
self._text_label.bind("<Leave>", self._on_leave)
self._text_label.bind("<Button-1>", self._clicked)
self._text_label.bind("<Button-1>", self._clicked)
self._create_bindings()
self._draw() # initial draw self._draw() # initial draw
if self._variable is not None: if self._variable is not None:
@ -136,6 +126,18 @@ class CTkOptionMenu(CTkBaseClass):
self._current_value = self._variable.get() self._current_value = self._variable.get()
self._text_label.configure(text=self._current_value) 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 == "<Enter>":
self._canvas.bind("<Enter>", self._on_enter)
self._text_label.bind("<Enter>", self._on_enter)
if sequence is None or sequence == "<Leave>":
self._canvas.bind("<Leave>", self._on_leave)
self._text_label.bind("<Leave>", self._on_leave)
if sequence is None or sequence == "<Button-1>":
self._canvas.bind("<Button-1>", self._clicked)
self._text_label.bind("<Button-1>", self._clicked)
def _create_grid(self): def _create_grid(self):
self._canvas.grid(row=0, column=0, sticky="nsew") 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: if self._state is not tkinter.DISABLED and len(self._values) > 0:
self._open_dropdown_menu() self._open_dropdown_menu()
def bind(self, sequence: str = None, command: Callable = None, add: str = None) -> str: def bind(self, sequence: str = None, command: Callable = None, add: Union[str, bool] = True):
""" called on the tkinter.Label and tkinter.Canvas """ """ called on the tkinter.Canvas """
canvas_bind_return = self._canvas.bind(sequence, command, add) if add != "+" or add is not True:
label_bind_return = self._text_label.bind(sequence, command, add) raise ValueError("'add' argument can only be '+' or True to preserve internal callbacks")
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 """ """ called on the tkinter.Label and tkinter.Canvas """
canvas_bind_return, label_bind_return = funcid.split(" + ") if funcid is not None:
self._canvas.unbind(sequence, canvas_bind_return) raise ValueError("'funcid' argument can only be None, because there is a bug in" +
self._text_label.unbind(sequence, label_bind_return) " 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): def focus(self):
return self._text_label.focus() return self._text_label.focus()

View File

@ -1,6 +1,6 @@
import tkinter import tkinter
import math import math
from typing import Union, Tuple, Optional, Literal from typing import Union, Tuple, Optional, Literal, Callable
from .core_rendering import CTkCanvas from .core_rendering import CTkCanvas
from .theme import ThemeManager from .theme import ThemeManager
@ -285,13 +285,18 @@ class CTkProgressBar(CTkBaseClass):
self._indeterminate_value += self._indeterminate_speed self._indeterminate_value += self._indeterminate_speed
self._draw() 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 """ """ 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): def unbind(self, sequence: str = None, funcid: str = None):
""" called on the tkinter.Canvas """ """ called on the tkinter.Label and 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)
def focus(self): def focus(self):
return self._canvas.focus() return self._canvas.focus()

View File

@ -99,10 +99,6 @@ class CTkRadioButton(CTkBaseClass):
self._canvas.grid(row=0, column=0) self._canvas.grid(row=0, column=0)
self._draw_engine = DrawEngine(self._canvas) self._draw_engine = DrawEngine(self._canvas)
self._canvas.bind("<Enter>", self._on_enter)
self._canvas.bind("<Leave>", self._on_leave)
self._canvas.bind("<Button-1>", self.invoke)
self._text_label = tkinter.Label(master=self, self._text_label = tkinter.Label(master=self,
bd=0, bd=0,
padx=0, padx=0,
@ -114,16 +110,25 @@ class CTkRadioButton(CTkBaseClass):
self._text_label.grid(row=0, column=2, sticky="w") self._text_label.grid(row=0, column=2, sticky="w")
self._text_label["anchor"] = "w" self._text_label["anchor"] = "w"
self._text_label.bind("<Enter>", self._on_enter)
self._text_label.bind("<Leave>", self._on_leave)
self._text_label.bind("<Button-1>", self.invoke)
if self._variable is not None: if self._variable is not None:
self._variable_callback_name = self._variable.trace_add("write", self._variable_callback) 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._check_state = True if self._variable.get() == self._value else False
self._draw() # initial draw self._create_bindings()
self._set_cursor() 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 == "<Enter>":
self._canvas.bind("<Enter>", self._on_enter)
self._text_label.bind("<Enter>", self._on_enter)
if sequence is None or sequence == "<Leave>":
self._canvas.bind("<Leave>", self._on_leave)
self._text_label.bind("<Leave>", self._on_leave)
if sequence is None or sequence == "<Button-1>":
self._canvas.bind("<Button-1>", self.invoke)
self._text_label.bind("<Button-1>", self.invoke)
def _set_scaling(self, *args, **kwargs): def _set_scaling(self, *args, **kwargs):
super()._set_scaling(*args, **kwargs) super()._set_scaling(*args, **kwargs)
@ -398,13 +403,21 @@ class CTkRadioButton(CTkBaseClass):
self._variable.set("") self._variable.set("")
self._variable_callback_blocked = False 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 """ """ 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): def unbind(self, sequence: str = None, funcid: str = None):
""" called on the tkinter.Canvas """ """ called on the tkinter.Label and 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)
self._text_label.unbind(sequence, None)
self._create_bindings(sequence=sequence) # restore internal callbacks for sequence
def focus(self): def focus(self):
return self._text_label.focus() return self._text_label.focus()

View File

@ -257,13 +257,13 @@ class CTkScrollbar(CTkBaseClass):
def get(self): def get(self):
return self._start_value, self._end_value 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 """ """ called on the tkinter.Canvas """
if add != "+" or add is not True: if add != "+" or add is not True:
raise ValueError("'add' argument can only be '+' or True to preserve internal callbacks") 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 """ """ called on the tkinter.Canvas, restores internal callbacks """
if funcid is not None: if funcid is not None:
raise ValueError("'funcid' argument can only be None, because there is a bug in" + raise ValueError("'funcid' argument can only be None, because there is a bug in" +

View File

@ -408,3 +408,9 @@ class CTkSegmentedButton(CTkFrame):
else: else:
raise ValueError(f"CTkSegmentedButton does not contain value '{value}'") 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

View File

@ -96,11 +96,7 @@ class CTkSlider(CTkBaseClass):
self._canvas.grid(column=0, row=0, rowspan=1, columnspan=1, sticky="nswe") self._canvas.grid(column=0, row=0, rowspan=1, columnspan=1, sticky="nswe")
self._draw_engine = DrawEngine(self._canvas) self._draw_engine = DrawEngine(self._canvas)
self._canvas.bind("<Enter>", self._on_enter) self._create_bindings()
self._canvas.bind("<Leave>", self._on_leave)
self._canvas.bind("<Button-1>", self._clicked)
self._canvas.bind("<B1-Motion>", self._clicked)
self._set_cursor() self._set_cursor()
self._draw() # initial draw self._draw() # initial draw
@ -110,6 +106,17 @@ class CTkSlider(CTkBaseClass):
self.set(self._variable.get(), from_variable_callback=True) self.set(self._variable.get(), from_variable_callback=True)
self._variable_callback_blocked = False 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 == "<Enter>":
self._canvas.bind("<Enter>", self._on_enter)
if sequence is None or sequence == "<Leave>":
self._canvas.bind("<Leave>", self._on_leave)
if sequence is None or sequence == "<Button-1>":
self._canvas.bind("<Button-1>", self._clicked)
if sequence is None or sequence == "<B1-Motion>":
self._canvas.bind("<B1-Motion>", self._clicked)
def _set_scaling(self, *args, **kwargs): def _set_scaling(self, *args, **kwargs):
super()._set_scaling(*args, **kwargs) super()._set_scaling(*args, **kwargs)
@ -366,13 +373,19 @@ class CTkSlider(CTkBaseClass):
if not self._variable_callback_blocked: if not self._variable_callback_blocked:
self.set(self._variable.get(), from_variable_callback=True) 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 """ """ 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): def unbind(self, sequence: str = None, funcid: str = None):
""" called on the tkinter.Canvas """ """ called on the tkinter.Label and 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)
self._create_bindings(sequence=sequence) # restore internal callbacks for sequence
def focus(self): def focus(self):
return self._canvas.focus() return self._canvas.focus()

View File

@ -106,10 +106,6 @@ class CTkSwitch(CTkBaseClass):
self._canvas.grid(row=0, column=0, sticky="") self._canvas.grid(row=0, column=0, sticky="")
self._draw_engine = DrawEngine(self._canvas) self._draw_engine = DrawEngine(self._canvas)
self._canvas.bind("<Enter>", self._on_enter)
self._canvas.bind("<Leave>", self._on_leave)
self._canvas.bind("<Button-1>", self.toggle)
self._text_label = tkinter.Label(master=self, self._text_label = tkinter.Label(master=self,
bd=0, bd=0,
padx=0, padx=0,
@ -121,16 +117,25 @@ class CTkSwitch(CTkBaseClass):
self._text_label.grid(row=0, column=2, sticky="w") self._text_label.grid(row=0, column=2, sticky="w")
self._text_label["anchor"] = "w" self._text_label["anchor"] = "w"
self._text_label.bind("<Enter>", self._on_enter)
self._text_label.bind("<Leave>", self._on_leave)
self._text_label.bind("<Button-1>", self.toggle)
if self._variable is not None and self._variable != "": if self._variable is not None and self._variable != "":
self._variable_callback_name = self._variable.trace_add("write", self._variable_callback) 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.c_heck_state = True if self._variable.get() == self._onvalue else False
self._draw() # initial draw self._create_bindings()
self._set_cursor() 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 == "<Enter>":
self._canvas.bind("<Enter>", self._on_enter)
self._text_label.bind("<Enter>", self._on_enter)
if sequence is None or sequence == "<Leave>":
self._canvas.bind("<Leave>", self._on_leave)
self._text_label.bind("<Leave>", self._on_leave)
if sequence is None or sequence == "<Button-1>":
self._canvas.bind("<Button-1>", self.toggle)
self._text_label.bind("<Button-1>", self.toggle)
def _set_scaling(self, *args, **kwargs): def _set_scaling(self, *args, **kwargs):
super()._set_scaling(*args, **kwargs) super()._set_scaling(*args, **kwargs)
@ -437,13 +442,21 @@ class CTkSwitch(CTkBaseClass):
elif self._variable.get() == self._offvalue: elif self._variable.get() == self._offvalue:
self.deselect(from_variable_callback=True) 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 """ """ 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): def unbind(self, sequence: str = None, funcid: str = None):
""" called on the tkinter.Canvas """ """ called on the tkinter.Label and 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)
self._text_label.unbind(sequence, None)
self._create_bindings(sequence=sequence) # restore internal callbacks for sequence
def focus(self): def focus(self):
return self._text_label.focus() return self._text_label.focus()

View File

@ -1,5 +1,5 @@
import tkinter import tkinter
from typing import Union, Tuple, Optional from typing import Union, Tuple, Optional, Callable
from .core_rendering import CTkCanvas from .core_rendering import CTkCanvas
from .ctk_scrollbar import CTkScrollbar from .ctk_scrollbar import CTkScrollbar
@ -86,7 +86,6 @@ class CTkTextbox(CTkBaseClass):
highlightthickness=0, highlightthickness=0,
relief="flat", relief="flat",
insertbackground=self._apply_appearance_mode(self._text_color), 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)) **pop_from_dict_by_set(kwargs, self._valid_tk_text_attributes))
check_kwargs_empty(kwargs, raise_error=True) 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), self._textbox.configure(fg=self._apply_appearance_mode(self._text_color),
bg=self._apply_appearance_mode(self._bg_color), bg=self._apply_appearance_mode(self._bg_color),
insertbackground=self._apply_appearance_mode(self._text_color)) insertbackground=self._apply_appearance_mode(self._text_color))
self._x_scrollbar.configure(fg_color=self._bg_color, scrollbar_color=self._scrollbar_button_color, self._x_scrollbar.configure(fg_color=self._bg_color, button_color=self._scrollbar_button_color,
scrollbar_hover_color=self._scrollbar_button_hover_color) button_hover_color=self._scrollbar_button_hover_color)
self._y_scrollbar.configure(fg_color=self._bg_color, scrollbar_color=self._scrollbar_button_color, self._y_scrollbar.configure(fg_color=self._bg_color, button_color=self._scrollbar_button_color,
scrollbar_hover_color=self._scrollbar_button_hover_color) button_hover_color=self._scrollbar_button_hover_color)
else: else:
self._canvas.itemconfig("inner_parts", self._canvas.itemconfig("inner_parts",
fill=self._apply_appearance_mode(self._fg_color), fill=self._apply_appearance_mode(self._fg_color),
@ -327,18 +326,18 @@ class CTkTextbox(CTkBaseClass):
else: else:
return super().cget(attribute_name) return super().cget(attribute_name)
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.Text """ """ 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 <KeyRelease>, allow only to add the binding to keep the _textbox_modified_event() being called def unbind(self, sequence: str = None, funcid: str = None):
if sequence == "<KeyRelease>": """ called on the tkinter.Label and tkinter.Canvas """
return self._textbox.bind(sequence, command, add="+") if funcid is not None:
else: raise ValueError("'funcid' argument can only be None, because there is a bug in" +
return self._textbox.bind(sequence, command, add) " tkinter and its not clear whether the internal callbacks will be unbinded or not")
self._textbox.unbind(sequence, None)
def unbind(self, sequence, funcid=None):
""" called on the tkinter.Text """
return self._textbox.unbind(sequence, funcid)
def focus(self): def focus(self):
return self._textbox.focus() return self._textbox.focus()