[feat] Event Generation on Canvas

For widgets based on the _canvas, attempting to use event_generate for
custom events failed. Typical notation in tkinter would allow such
bindings but in customtkinter, operations are performed on hidden
widgets. Thus a function override is required to operate as expected.

Widgets that are not based on _canvas or where a combination of hidden
widgets were not updated.

Signed off by Trask Crane (hcrane3@gatech.edu)
This commit is contained in:
Crane, Trask
2023-06-04 23:48:43 -04:00
parent b779c57e61
commit 4838b01d6a
7 changed files with 26 additions and 6 deletions

View File

@@ -107,13 +107,13 @@ class CTk(tkinter.Tk, CTkAppearanceModeBaseClass, CTkScalingBaseClass):
def _set_scaling(self, new_widget_scaling, new_window_scaling):
super()._set_scaling(new_widget_scaling, new_window_scaling)
# Force new dimensions on window by using min, max, and geometry. Without min, max it won't work.
# Force new dimensions on window by using abs_min, max, and geometry. Without abs_min, max it won't work.
super().minsize(self._apply_window_scaling(self._current_width), self._apply_window_scaling(self._current_height))
super().maxsize(self._apply_window_scaling(self._current_width), self._apply_window_scaling(self._current_height))
super().geometry(f"{self._apply_window_scaling(self._current_width)}x{self._apply_window_scaling(self._current_height)}")
# set new scaled min and max with delay (delay prevents weird bug where window dimensions snap to unscaled dimensions when mouse releases window)
# set new scaled abs_min and max with delay (delay prevents weird bug where window dimensions snap to unscaled dimensions when mouse releases window)
self.after(1000, self._set_scaled_min_max) # Why 1000ms delay? Experience! (Everything tested on Windows 11)
def block_update_dimensions_event(self):
@@ -196,7 +196,7 @@ class CTk(tkinter.Tk, CTkAppearanceModeBaseClass, CTkScalingBaseClass):
# update width and height attributes
width, height, x, y = self._parse_geometry_string(geometry_string)
if width is not None and height is not None:
self._current_width = max(self._min_width, min(width, self._max_width)) # bound value between min and max
self._current_width = max(self._min_width, min(width, self._max_width)) # bound value between abs_min and max
self._current_height = max(self._min_height, min(height, self._max_height))
else:
return self._reverse_geometry_scaling(super().geometry())

View File

@@ -109,13 +109,13 @@ class CTkToplevel(tkinter.Toplevel, CTkAppearanceModeBaseClass, CTkScalingBaseCl
def _set_scaling(self, new_widget_scaling, new_window_scaling):
super()._set_scaling(new_widget_scaling, new_window_scaling)
# Force new dimensions on window by using min, max, and geometry. Without min, max it won't work.
# Force new dimensions on window by using abs_min, max, and geometry. Without abs_min, max it won't work.
super().minsize(self._apply_window_scaling(self._current_width), self._apply_window_scaling(self._current_height))
super().maxsize(self._apply_window_scaling(self._current_width), self._apply_window_scaling(self._current_height))
super().geometry(f"{self._apply_window_scaling(self._current_width)}x{self._apply_window_scaling(self._current_height)}")
# set new scaled min and max with delay (delay prevents weird bug where window dimensions snap to unscaled dimensions when mouse releases window)
# set new scaled abs_min and max with delay (delay prevents weird bug where window dimensions snap to unscaled dimensions when mouse releases window)
self.after(1000, self._set_scaled_min_max) # Why 1000ms delay? Experience! (Everything tested on Windows 11)
def block_update_dimensions_event(self):
@@ -137,7 +137,7 @@ class CTkToplevel(tkinter.Toplevel, CTkAppearanceModeBaseClass, CTkScalingBaseCl
# update width and height attributes
width, height, x, y = self._parse_geometry_string(geometry_string)
if width is not None and height is not None:
self._current_width = max(self._min_width, min(width, self._max_width)) # bound value between min and max
self._current_width = max(self._min_width, min(width, self._max_width)) # bound value between abs_min and max
self._current_height = max(self._min_height, min(height, self._max_height))
else:
return self._reverse_geometry_scaling(super().geometry())

View File

@@ -194,3 +194,7 @@ class CTkFrame(CTkBaseClass):
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 event_generate(self, sequence, **kw):
""" called on the tkinter.Canvas """
self._canvas.event_generate(sequence, **kw)

View File

@@ -281,6 +281,10 @@ class CTkLabel(CTkBaseClass):
self._canvas.unbind(sequence, None)
self._label.unbind(sequence, None)
def event_generate(self, sequence, **kw):
""" called on the tkinter.Canvas """
self._canvas.event_generate(sequence, **kw)
def focus(self):
return self._label.focus()

View File

@@ -302,6 +302,10 @@ class CTkProgressBar(CTkBaseClass):
" tkinter and its not clear whether the internal callbacks will be unbinded or not")
self._canvas.unbind(sequence, None)
def event_generate(self, sequence, **kw):
""" called on the tkinter.Canvas """
self._canvas.event_generate(sequence, **kw)
def focus(self):
return self._canvas.focus()

View File

@@ -271,6 +271,10 @@ class CTkScrollbar(CTkBaseClass):
self._canvas.unbind(sequence, None) # unbind all callbacks for sequence
self._create_bindings(sequence=sequence) # restore internal callbacks for sequence
def event_generate(self, sequence, **kw):
""" called on the tkinter.Canvas """
self._canvas.event_generate(sequence, **kw)
def focus(self):
return self._canvas.focus()

View File

@@ -387,6 +387,10 @@ class CTkSlider(CTkBaseClass):
self._canvas.unbind(sequence, None)
self._create_bindings(sequence=sequence) # restore internal callbacks for sequence
def event_generate(self, sequence, **kw):
""" called on the tkinter.Canvas """
self._canvas.event_generate(sequence, **kw)
def focus(self):
return self._canvas.focus()