From d2fab58669b4b032b7a815aee3b8949405430bca Mon Sep 17 00:00:00 2001 From: LYMwyh <122608577+LYMwyh@users.noreply.github.com> Date: Fri, 16 Aug 2024 17:52:25 +0800 Subject: [PATCH 1/7] add sensitive scrollbar feature --- .../windows/widgets/ctk_scrollable_frame.py | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/customtkinter/windows/widgets/ctk_scrollable_frame.py b/customtkinter/windows/widgets/ctk_scrollable_frame.py index eede0911..dc537c80 100644 --- a/customtkinter/windows/widgets/ctk_scrollable_frame.py +++ b/customtkinter/windows/widgets/ctk_scrollable_frame.py @@ -102,7 +102,7 @@ def _create_grid(self): self._parent_frame.grid_columnconfigure(0, weight=1) self._parent_frame.grid_rowconfigure(1, weight=1) self._parent_canvas.grid(row=1, column=0, sticky="nsew", padx=border_spacing, pady=(border_spacing, 0)) - self._scrollbar.grid(row=2, column=0, sticky="nsew", padx=border_spacing) + self._update_scrollbar(border_spacing) if self._label_text is not None and self._label_text != "": self._label.grid(row=0, column=0, sticky="ew", padx=border_spacing, pady=border_spacing) @@ -113,7 +113,7 @@ def _create_grid(self): self._parent_frame.grid_columnconfigure(0, weight=1) self._parent_frame.grid_rowconfigure(1, weight=1) self._parent_canvas.grid(row=1, column=0, sticky="nsew", padx=(border_spacing, 0), pady=border_spacing) - self._scrollbar.grid(row=1, column=1, sticky="nsew", pady=border_spacing) + self._update_scrollbar(border_spacing) if self._label_text is not None and self._label_text != "": self._label.grid(row=0, column=0, columnspan=2, sticky="ew", padx=border_spacing, pady=border_spacing) @@ -235,8 +235,27 @@ def cget(self, attribute_name: str): def _fit_frame_dimensions_to_canvas(self, event): if self._orientation == "horizontal": self._parent_canvas.itemconfigure(self._create_window_id, height=self._parent_canvas.winfo_height()) + if self._parent_canvas.xview() != (0.0, 1.0): + self._update_scrollbar() elif self._orientation == "vertical": self._parent_canvas.itemconfigure(self._create_window_id, width=self._parent_canvas.winfo_width()) + if self._parent_canvas.yview() != (0.0, 1.0): + self._update_scrollbar() + + def _update_scrollbar(self, border_spacing=None): + if border_spacing is None: + border_spacing = self._apply_widget_scaling( + self._parent_frame.cget("corner_radius") + self._parent_frame.cget("border_width")) + if self._orientation == "horizontal": + if self._parent_canvas.xview() != (0.0, 1.0): + self._scrollbar.grid(row=2, column=0, sticky="nsew", padx=border_spacing) + else: + self._scrollbar.grid_remove() + elif self._orientation == "vertical": + if self._parent_canvas.yview() != (0.0, 1.0): + self._scrollbar.grid(row=1, column=1, sticky="nsew", padx=border_spacing) + else: + self._scrollbar.grid_remove() def _set_scroll_increments(self): if sys.platform.startswith("win"): From a5aeb05c9296e1bc986fc83bd5a2e83f7c851e7f Mon Sep 17 00:00:00 2001 From: LYMwyh <122608577+LYMwyh@users.noreply.github.com> Date: Sat, 17 Aug 2024 16:38:50 +0800 Subject: [PATCH 2/7] add grid_configure to specify the margin(padx/pady) of the _parent_canvas while scrollbar isn't displayed --- customtkinter/windows/widgets/ctk_scrollable_frame.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/customtkinter/windows/widgets/ctk_scrollable_frame.py b/customtkinter/windows/widgets/ctk_scrollable_frame.py index dc537c80..dca56b9d 100644 --- a/customtkinter/windows/widgets/ctk_scrollable_frame.py +++ b/customtkinter/windows/widgets/ctk_scrollable_frame.py @@ -248,13 +248,17 @@ def _update_scrollbar(self, border_spacing=None): self._parent_frame.cget("corner_radius") + self._parent_frame.cget("border_width")) if self._orientation == "horizontal": if self._parent_canvas.xview() != (0.0, 1.0): + self._parent_canvas.grid_configure(pady=(border_spacing, 0)) self._scrollbar.grid(row=2, column=0, sticky="nsew", padx=border_spacing) else: + self._parent_canvas.grid_configure(pady=border_spacing) self._scrollbar.grid_remove() elif self._orientation == "vertical": if self._parent_canvas.yview() != (0.0, 1.0): + self._parent_canvas.grid_configure(padx=(border_spacing, 0)) self._scrollbar.grid(row=1, column=1, sticky="nsew", padx=border_spacing) else: + self._parent_canvas.grid_configure(padx=border_spacing) self._scrollbar.grid_remove() def _set_scroll_increments(self): From 5be2036cb1f2b30e85de60f3f24576d85db20d9b Mon Sep 17 00:00:00 2001 From: LYMwyh <122608577+LYMwyh@users.noreply.github.com> Date: Sat, 17 Aug 2024 16:47:05 +0800 Subject: [PATCH 3/7] modify grid_configure for incorrect paddings because of scrollbar orientation has changed after initialization --- customtkinter/windows/widgets/ctk_scrollable_frame.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/customtkinter/windows/widgets/ctk_scrollable_frame.py b/customtkinter/windows/widgets/ctk_scrollable_frame.py index dca56b9d..7e4fe884 100644 --- a/customtkinter/windows/widgets/ctk_scrollable_frame.py +++ b/customtkinter/windows/widgets/ctk_scrollable_frame.py @@ -248,17 +248,17 @@ def _update_scrollbar(self, border_spacing=None): self._parent_frame.cget("corner_radius") + self._parent_frame.cget("border_width")) if self._orientation == "horizontal": if self._parent_canvas.xview() != (0.0, 1.0): - self._parent_canvas.grid_configure(pady=(border_spacing, 0)) + self._parent_canvas.grid_configure(padx=border_spacing, pady=(border_spacing, 0)) self._scrollbar.grid(row=2, column=0, sticky="nsew", padx=border_spacing) else: - self._parent_canvas.grid_configure(pady=border_spacing) + self._parent_canvas.grid_configure(padx=border_spacing, pady=border_spacing) self._scrollbar.grid_remove() elif self._orientation == "vertical": if self._parent_canvas.yview() != (0.0, 1.0): - self._parent_canvas.grid_configure(padx=(border_spacing, 0)) + self._parent_canvas.grid_configure(padx=(border_spacing, 0), pady=(border_spacing, 0)) self._scrollbar.grid(row=1, column=1, sticky="nsew", padx=border_spacing) else: - self._parent_canvas.grid_configure(padx=border_spacing) + self._parent_canvas.grid_configure(padx=border_spacing, pady=(border_spacing, 0)) self._scrollbar.grid_remove() def _set_scroll_increments(self): From dc663f4bd64d90b4179a6ea3aa471d5dd66b63a5 Mon Sep 17 00:00:00 2001 From: LYMwyh <122608577+LYMwyh@users.noreply.github.com> Date: Sun, 1 Sep 2024 14:43:36 +0800 Subject: [PATCH 4/7] update ctk_scrollable_frame.py --- .../windows/widgets/ctk_scrollable_frame.py | 43 ++++++++----------- 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/customtkinter/windows/widgets/ctk_scrollable_frame.py b/customtkinter/windows/widgets/ctk_scrollable_frame.py index 7e4fe884..1fa66325 100644 --- a/customtkinter/windows/widgets/ctk_scrollable_frame.py +++ b/customtkinter/windows/widgets/ctk_scrollable_frame.py @@ -72,7 +72,7 @@ def __init__(self, self._parent_canvas.configure(width=self._apply_widget_scaling(self._desired_width), height=self._apply_widget_scaling(self._desired_height)) - self.bind("", lambda e: self._parent_canvas.configure(scrollregion=self._parent_canvas.bbox("all"))) + self.bind("", self._check_scroll_region) self._parent_canvas.bind("", self._fit_frame_dimensions_to_canvas) self.bind_all("", self._mouse_wheel_all, add="+") self.bind_all("", self._keyboard_shift_press_all, add="+") @@ -102,7 +102,7 @@ def _create_grid(self): self._parent_frame.grid_columnconfigure(0, weight=1) self._parent_frame.grid_rowconfigure(1, weight=1) self._parent_canvas.grid(row=1, column=0, sticky="nsew", padx=border_spacing, pady=(border_spacing, 0)) - self._update_scrollbar(border_spacing) + self._scrollbar.grid(row=2, column=0, sticky="nsew", padx=border_spacing) if self._label_text is not None and self._label_text != "": self._label.grid(row=0, column=0, sticky="ew", padx=border_spacing, pady=border_spacing) @@ -113,7 +113,7 @@ def _create_grid(self): self._parent_frame.grid_columnconfigure(0, weight=1) self._parent_frame.grid_rowconfigure(1, weight=1) self._parent_canvas.grid(row=1, column=0, sticky="nsew", padx=(border_spacing, 0), pady=border_spacing) - self._update_scrollbar(border_spacing) + self._scrollbar.grid(row=1, column=1, sticky="nsew", pady=border_spacing) if self._label_text is not None and self._label_text != "": self._label.grid(row=0, column=0, columnspan=2, sticky="ew", padx=border_spacing, pady=border_spacing) @@ -235,31 +235,24 @@ def cget(self, attribute_name: str): def _fit_frame_dimensions_to_canvas(self, event): if self._orientation == "horizontal": self._parent_canvas.itemconfigure(self._create_window_id, height=self._parent_canvas.winfo_height()) - if self._parent_canvas.xview() != (0.0, 1.0): - self._update_scrollbar() elif self._orientation == "vertical": self._parent_canvas.itemconfigure(self._create_window_id, width=self._parent_canvas.winfo_width()) - if self._parent_canvas.yview() != (0.0, 1.0): - self._update_scrollbar() + self._check_scroll_necessity() - def _update_scrollbar(self, border_spacing=None): - if border_spacing is None: - border_spacing = self._apply_widget_scaling( - self._parent_frame.cget("corner_radius") + self._parent_frame.cget("border_width")) - if self._orientation == "horizontal": - if self._parent_canvas.xview() != (0.0, 1.0): - self._parent_canvas.grid_configure(padx=border_spacing, pady=(border_spacing, 0)) - self._scrollbar.grid(row=2, column=0, sticky="nsew", padx=border_spacing) - else: - self._parent_canvas.grid_configure(padx=border_spacing, pady=border_spacing) - self._scrollbar.grid_remove() - elif self._orientation == "vertical": - if self._parent_canvas.yview() != (0.0, 1.0): - self._parent_canvas.grid_configure(padx=(border_spacing, 0), pady=(border_spacing, 0)) - self._scrollbar.grid(row=1, column=1, sticky="nsew", padx=border_spacing) - else: - self._parent_canvas.grid_configure(padx=border_spacing, pady=(border_spacing, 0)) - self._scrollbar.grid_remove() + def _check_scroll_necessity(self): + canvas_height = self._parent_canvas.winfo_height() + content_height = self.winfo_height() + + if content_height <= canvas_height: + self._scrollbar.grid_remove() + self._parent_canvas.configure(yscrollcommand=None) + else: + self._scrollbar.grid() + self._parent_canvas.configure(yscrollcommand=self._scrollbar.set) + + def _check_scroll_region(self, event): + self._parent_canvas.configure(scrollregion=self._parent_canvas.bbox("all")) + self._check_scroll_necessity() def _set_scroll_increments(self): if sys.platform.startswith("win"): From 0eda510f676958e28a356d2ce64102963d1cfe96 Mon Sep 17 00:00:00 2001 From: Derek Liu <122608577+HelloWorld-er@users.noreply.github.com> Date: Sun, 1 Sep 2024 14:48:50 +0800 Subject: [PATCH 5/7] Delete customtkinter/windows/widgets/ctk_scrollable_frame.py --- .../windows/widgets/ctk_scrollable_frame.py | 332 ------------------ 1 file changed, 332 deletions(-) delete mode 100644 customtkinter/windows/widgets/ctk_scrollable_frame.py diff --git a/customtkinter/windows/widgets/ctk_scrollable_frame.py b/customtkinter/windows/widgets/ctk_scrollable_frame.py deleted file mode 100644 index 1fa66325..00000000 --- a/customtkinter/windows/widgets/ctk_scrollable_frame.py +++ /dev/null @@ -1,332 +0,0 @@ -from typing import Union, Tuple, Optional, Any -try: - from typing import Literal -except ImportError: - from typing_extensions import Literal -import tkinter -import sys - -from .ctk_frame import CTkFrame -from .ctk_scrollbar import CTkScrollbar -from .appearance_mode import CTkAppearanceModeBaseClass -from .scaling import CTkScalingBaseClass -from .core_widget_classes import CTkBaseClass -from .ctk_label import CTkLabel -from .font import CTkFont -from .theme import ThemeManager - - -class CTkScrollableFrame(tkinter.Frame, CTkAppearanceModeBaseClass, CTkScalingBaseClass): - def __init__(self, - master: Any, - width: int = 200, - height: int = 200, - corner_radius: Optional[Union[int, str]] = None, - border_width: Optional[Union[int, str]] = None, - - bg_color: Union[str, Tuple[str, str]] = "transparent", - fg_color: Optional[Union[str, Tuple[str, str]]] = None, - border_color: Optional[Union[str, Tuple[str, str]]] = None, - scrollbar_fg_color: Optional[Union[str, Tuple[str, str]]] = None, - scrollbar_button_color: Optional[Union[str, Tuple[str, str]]] = None, - scrollbar_button_hover_color: Optional[Union[str, Tuple[str, str]]] = None, - label_fg_color: Optional[Union[str, Tuple[str, str]]] = None, - label_text_color: Optional[Union[str, Tuple[str, str]]] = None, - - label_text: str = "", - label_font: Optional[Union[tuple, CTkFont]] = None, - label_anchor: str = "center", - orientation: Literal["vertical", "horizontal"] = "vertical"): - - self._orientation = orientation - - # dimensions independent of scaling - self._desired_width = width # _desired_width and _desired_height, represent desired size set by width and height - self._desired_height = height - - self._parent_frame = CTkFrame(master=master, width=0, height=0, corner_radius=corner_radius, - border_width=border_width, bg_color=bg_color, fg_color=fg_color, border_color=border_color) - self._parent_canvas = tkinter.Canvas(master=self._parent_frame, highlightthickness=0) - self._set_scroll_increments() - - if self._orientation == "horizontal": - self._scrollbar = CTkScrollbar(master=self._parent_frame, orientation="horizontal", command=self._parent_canvas.xview, - fg_color=scrollbar_fg_color, button_color=scrollbar_button_color, button_hover_color=scrollbar_button_hover_color) - self._parent_canvas.configure(xscrollcommand=self._scrollbar.set) - elif self._orientation == "vertical": - self._scrollbar = CTkScrollbar(master=self._parent_frame, orientation="vertical", command=self._parent_canvas.yview, - fg_color=scrollbar_fg_color, button_color=scrollbar_button_color, button_hover_color=scrollbar_button_hover_color) - self._parent_canvas.configure(yscrollcommand=self._scrollbar.set) - - self._label_text = label_text - self._label = CTkLabel(self._parent_frame, text=label_text, anchor=label_anchor, font=label_font, - corner_radius=self._parent_frame.cget("corner_radius"), text_color=label_text_color, - fg_color=ThemeManager.theme["CTkScrollableFrame"]["label_fg_color"] if label_fg_color is None else label_fg_color) - - tkinter.Frame.__init__(self, master=self._parent_canvas, highlightthickness=0) - CTkAppearanceModeBaseClass.__init__(self) - CTkScalingBaseClass.__init__(self, scaling_type="widget") - - self._create_grid() - - self._parent_canvas.configure(width=self._apply_widget_scaling(self._desired_width), - height=self._apply_widget_scaling(self._desired_height)) - - self.bind("", self._check_scroll_region) - self._parent_canvas.bind("", self._fit_frame_dimensions_to_canvas) - self.bind_all("", self._mouse_wheel_all, add="+") - self.bind_all("", self._keyboard_shift_press_all, add="+") - self.bind_all("", self._keyboard_shift_press_all, add="+") - self.bind_all("", self._keyboard_shift_release_all, add="+") - self.bind_all("", self._keyboard_shift_release_all, add="+") - self._create_window_id = self._parent_canvas.create_window(0, 0, window=self, anchor="nw") - - if self._parent_frame.cget("fg_color") == "transparent": - tkinter.Frame.configure(self, bg=self._apply_appearance_mode(self._parent_frame.cget("bg_color"))) - self._parent_canvas.configure(bg=self._apply_appearance_mode(self._parent_frame.cget("bg_color"))) - else: - tkinter.Frame.configure(self, bg=self._apply_appearance_mode(self._parent_frame.cget("fg_color"))) - self._parent_canvas.configure(bg=self._apply_appearance_mode(self._parent_frame.cget("fg_color"))) - - self._shift_pressed = False - - def destroy(self): - tkinter.Frame.destroy(self) - CTkAppearanceModeBaseClass.destroy(self) - CTkScalingBaseClass.destroy(self) - - def _create_grid(self): - border_spacing = self._apply_widget_scaling(self._parent_frame.cget("corner_radius") + self._parent_frame.cget("border_width")) - - if self._orientation == "horizontal": - self._parent_frame.grid_columnconfigure(0, weight=1) - self._parent_frame.grid_rowconfigure(1, weight=1) - self._parent_canvas.grid(row=1, column=0, sticky="nsew", padx=border_spacing, pady=(border_spacing, 0)) - self._scrollbar.grid(row=2, column=0, sticky="nsew", padx=border_spacing) - - if self._label_text is not None and self._label_text != "": - self._label.grid(row=0, column=0, sticky="ew", padx=border_spacing, pady=border_spacing) - else: - self._label.grid_forget() - - elif self._orientation == "vertical": - self._parent_frame.grid_columnconfigure(0, weight=1) - self._parent_frame.grid_rowconfigure(1, weight=1) - self._parent_canvas.grid(row=1, column=0, sticky="nsew", padx=(border_spacing, 0), pady=border_spacing) - self._scrollbar.grid(row=1, column=1, sticky="nsew", pady=border_spacing) - - if self._label_text is not None and self._label_text != "": - self._label.grid(row=0, column=0, columnspan=2, sticky="ew", padx=border_spacing, pady=border_spacing) - else: - self._label.grid_forget() - - def _set_appearance_mode(self, mode_string): - super()._set_appearance_mode(mode_string) - - if self._parent_frame.cget("fg_color") == "transparent": - tkinter.Frame.configure(self, bg=self._apply_appearance_mode(self._parent_frame.cget("bg_color"))) - self._parent_canvas.configure(bg=self._apply_appearance_mode(self._parent_frame.cget("bg_color"))) - else: - tkinter.Frame.configure(self, bg=self._apply_appearance_mode(self._parent_frame.cget("fg_color"))) - self._parent_canvas.configure(bg=self._apply_appearance_mode(self._parent_frame.cget("fg_color"))) - - def _set_scaling(self, new_widget_scaling, new_window_scaling): - super()._set_scaling(new_widget_scaling, new_window_scaling) - - self._parent_canvas.configure(width=self._apply_widget_scaling(self._desired_width), - height=self._apply_widget_scaling(self._desired_height)) - - def _set_dimensions(self, width=None, height=None): - if width is not None: - self._desired_width = width - if height is not None: - self._desired_height = height - - self._parent_canvas.configure(width=self._apply_widget_scaling(self._desired_width), - height=self._apply_widget_scaling(self._desired_height)) - - def configure(self, **kwargs): - if "width" in kwargs: - self._set_dimensions(width=kwargs.pop("width")) - - if "height" in kwargs: - self._set_dimensions(height=kwargs.pop("height")) - - if "corner_radius" in kwargs: - new_corner_radius = kwargs.pop("corner_radius") - self._parent_frame.configure(corner_radius=new_corner_radius) - if self._label is not None: - self._label.configure(corner_radius=new_corner_radius) - self._create_grid() - - if "border_width" in kwargs: - self._parent_frame.configure(border_width=kwargs.pop("border_width")) - self._create_grid() - - if "fg_color" in kwargs: - self._parent_frame.configure(fg_color=kwargs.pop("fg_color")) - - if self._parent_frame.cget("fg_color") == "transparent": - tkinter.Frame.configure(self, bg=self._apply_appearance_mode(self._parent_frame.cget("bg_color"))) - self._parent_canvas.configure(bg=self._apply_appearance_mode(self._parent_frame.cget("bg_color"))) - else: - tkinter.Frame.configure(self, bg=self._apply_appearance_mode(self._parent_frame.cget("fg_color"))) - self._parent_canvas.configure(bg=self._apply_appearance_mode(self._parent_frame.cget("fg_color"))) - - for child in self.winfo_children(): - if isinstance(child, CTkBaseClass): - child.configure(bg_color=self._parent_frame.cget("fg_color")) - - if "scrollbar_fg_color" in kwargs: - self._scrollbar.configure(fg_color=kwargs.pop("scrollbar_fg_color")) - - if "scrollbar_button_color" in kwargs: - self._scrollbar.configure(button_color=kwargs.pop("scrollbar_button_color")) - - if "scrollbar_button_hover_color" in kwargs: - self._scrollbar.configure(button_hover_color=kwargs.pop("scrollbar_button_hover_color")) - - if "label_text" in kwargs: - self._label_text = kwargs.pop("label_text") - self._label.configure(text=self._label_text) - self._create_grid() - - if "label_font" in kwargs: - self._label.configure(font=kwargs.pop("label_font")) - - if "label_text_color" in kwargs: - self._label.configure(text_color=kwargs.pop("label_text_color")) - - if "label_fg_color" in kwargs: - self._label.configure(fg_color=kwargs.pop("label_fg_color")) - - if "label_anchor" in kwargs: - self._label.configure(anchor=kwargs.pop("label_anchor")) - - self._parent_frame.configure(**kwargs) - - def cget(self, attribute_name: str): - if attribute_name == "width": - return self._desired_width - elif attribute_name == "height": - return self._desired_height - - elif attribute_name == "label_text": - return self._label_text - elif attribute_name == "label_font": - return self._label.cget("font") - elif attribute_name == "label_text_color": - return self._label.cget("_text_color") - elif attribute_name == "label_fg_color": - return self._label.cget("fg_color") - elif attribute_name == "label_anchor": - return self._label.cget("anchor") - - elif attribute_name.startswith("scrollbar_fg_color"): - return self._scrollbar.cget("fg_color") - elif attribute_name.startswith("scrollbar_button_color"): - return self._scrollbar.cget("button_color") - elif attribute_name.startswith("scrollbar_button_hover_color"): - return self._scrollbar.cget("button_hover_color") - - else: - return self._parent_frame.cget(attribute_name) - - def _fit_frame_dimensions_to_canvas(self, event): - if self._orientation == "horizontal": - self._parent_canvas.itemconfigure(self._create_window_id, height=self._parent_canvas.winfo_height()) - elif self._orientation == "vertical": - self._parent_canvas.itemconfigure(self._create_window_id, width=self._parent_canvas.winfo_width()) - self._check_scroll_necessity() - - def _check_scroll_necessity(self): - canvas_height = self._parent_canvas.winfo_height() - content_height = self.winfo_height() - - if content_height <= canvas_height: - self._scrollbar.grid_remove() - self._parent_canvas.configure(yscrollcommand=None) - else: - self._scrollbar.grid() - self._parent_canvas.configure(yscrollcommand=self._scrollbar.set) - - def _check_scroll_region(self, event): - self._parent_canvas.configure(scrollregion=self._parent_canvas.bbox("all")) - self._check_scroll_necessity() - - def _set_scroll_increments(self): - if sys.platform.startswith("win"): - self._parent_canvas.configure(xscrollincrement=1, yscrollincrement=1) - elif sys.platform == "darwin": - self._parent_canvas.configure(xscrollincrement=4, yscrollincrement=8) - - def _mouse_wheel_all(self, event): - if self.check_if_master_is_canvas(event.widget): - if sys.platform.startswith("win"): - if self._shift_pressed: - if self._parent_canvas.xview() != (0.0, 1.0): - self._parent_canvas.xview("scroll", -int(event.delta / 6), "units") - else: - if self._parent_canvas.yview() != (0.0, 1.0): - self._parent_canvas.yview("scroll", -int(event.delta / 6), "units") - elif sys.platform == "darwin": - if self._shift_pressed: - if self._parent_canvas.xview() != (0.0, 1.0): - self._parent_canvas.xview("scroll", -event.delta, "units") - else: - if self._parent_canvas.yview() != (0.0, 1.0): - self._parent_canvas.yview("scroll", -event.delta, "units") - else: - if self._shift_pressed: - if self._parent_canvas.xview() != (0.0, 1.0): - self._parent_canvas.xview("scroll", -event.delta, "units") - else: - if self._parent_canvas.yview() != (0.0, 1.0): - self._parent_canvas.yview("scroll", -event.delta, "units") - - def _keyboard_shift_press_all(self, event): - self._shift_pressed = True - - def _keyboard_shift_release_all(self, event): - self._shift_pressed = False - - def check_if_master_is_canvas(self, widget): - if widget == self._parent_canvas: - return True - elif widget.master is not None: - return self.check_if_master_is_canvas(widget.master) - else: - return False - - def pack(self, **kwargs): - self._parent_frame.pack(**kwargs) - - def place(self, **kwargs): - self._parent_frame.place(**kwargs) - - def grid(self, **kwargs): - self._parent_frame.grid(**kwargs) - - def pack_forget(self): - self._parent_frame.pack_forget() - - def place_forget(self, **kwargs): - self._parent_frame.place_forget() - - def grid_forget(self, **kwargs): - self._parent_frame.grid_forget() - - def grid_remove(self, **kwargs): - self._parent_frame.grid_remove() - - def grid_propagate(self, **kwargs): - self._parent_frame.grid_propagate() - - def grid_info(self, **kwargs): - return self._parent_frame.grid_info() - - def lift(self, aboveThis=None): - self._parent_frame.lift(aboveThis) - - def lower(self, belowThis=None): - self._parent_frame.lower(belowThis) From 0fe713afbadb50813c7beb7bb4d540619ef27d52 Mon Sep 17 00:00:00 2001 From: LYMwyh <122608577+LYMwyh@users.noreply.github.com> Date: Sun, 1 Sep 2024 14:57:06 +0800 Subject: [PATCH 6/7] reset ctk_scrollable_frame.py --- .../windows/widgets/ctk_scrollable_frame.py | 316 ++++++++++++++++++ 1 file changed, 316 insertions(+) create mode 100644 customtkinter/windows/widgets/ctk_scrollable_frame.py diff --git a/customtkinter/windows/widgets/ctk_scrollable_frame.py b/customtkinter/windows/widgets/ctk_scrollable_frame.py new file mode 100644 index 00000000..eede0911 --- /dev/null +++ b/customtkinter/windows/widgets/ctk_scrollable_frame.py @@ -0,0 +1,316 @@ +from typing import Union, Tuple, Optional, Any +try: + from typing import Literal +except ImportError: + from typing_extensions import Literal +import tkinter +import sys + +from .ctk_frame import CTkFrame +from .ctk_scrollbar import CTkScrollbar +from .appearance_mode import CTkAppearanceModeBaseClass +from .scaling import CTkScalingBaseClass +from .core_widget_classes import CTkBaseClass +from .ctk_label import CTkLabel +from .font import CTkFont +from .theme import ThemeManager + + +class CTkScrollableFrame(tkinter.Frame, CTkAppearanceModeBaseClass, CTkScalingBaseClass): + def __init__(self, + master: Any, + width: int = 200, + height: int = 200, + corner_radius: Optional[Union[int, str]] = None, + border_width: Optional[Union[int, str]] = None, + + bg_color: Union[str, Tuple[str, str]] = "transparent", + fg_color: Optional[Union[str, Tuple[str, str]]] = None, + border_color: Optional[Union[str, Tuple[str, str]]] = None, + scrollbar_fg_color: Optional[Union[str, Tuple[str, str]]] = None, + scrollbar_button_color: Optional[Union[str, Tuple[str, str]]] = None, + scrollbar_button_hover_color: Optional[Union[str, Tuple[str, str]]] = None, + label_fg_color: Optional[Union[str, Tuple[str, str]]] = None, + label_text_color: Optional[Union[str, Tuple[str, str]]] = None, + + label_text: str = "", + label_font: Optional[Union[tuple, CTkFont]] = None, + label_anchor: str = "center", + orientation: Literal["vertical", "horizontal"] = "vertical"): + + self._orientation = orientation + + # dimensions independent of scaling + self._desired_width = width # _desired_width and _desired_height, represent desired size set by width and height + self._desired_height = height + + self._parent_frame = CTkFrame(master=master, width=0, height=0, corner_radius=corner_radius, + border_width=border_width, bg_color=bg_color, fg_color=fg_color, border_color=border_color) + self._parent_canvas = tkinter.Canvas(master=self._parent_frame, highlightthickness=0) + self._set_scroll_increments() + + if self._orientation == "horizontal": + self._scrollbar = CTkScrollbar(master=self._parent_frame, orientation="horizontal", command=self._parent_canvas.xview, + fg_color=scrollbar_fg_color, button_color=scrollbar_button_color, button_hover_color=scrollbar_button_hover_color) + self._parent_canvas.configure(xscrollcommand=self._scrollbar.set) + elif self._orientation == "vertical": + self._scrollbar = CTkScrollbar(master=self._parent_frame, orientation="vertical", command=self._parent_canvas.yview, + fg_color=scrollbar_fg_color, button_color=scrollbar_button_color, button_hover_color=scrollbar_button_hover_color) + self._parent_canvas.configure(yscrollcommand=self._scrollbar.set) + + self._label_text = label_text + self._label = CTkLabel(self._parent_frame, text=label_text, anchor=label_anchor, font=label_font, + corner_radius=self._parent_frame.cget("corner_radius"), text_color=label_text_color, + fg_color=ThemeManager.theme["CTkScrollableFrame"]["label_fg_color"] if label_fg_color is None else label_fg_color) + + tkinter.Frame.__init__(self, master=self._parent_canvas, highlightthickness=0) + CTkAppearanceModeBaseClass.__init__(self) + CTkScalingBaseClass.__init__(self, scaling_type="widget") + + self._create_grid() + + self._parent_canvas.configure(width=self._apply_widget_scaling(self._desired_width), + height=self._apply_widget_scaling(self._desired_height)) + + self.bind("", lambda e: self._parent_canvas.configure(scrollregion=self._parent_canvas.bbox("all"))) + self._parent_canvas.bind("", self._fit_frame_dimensions_to_canvas) + self.bind_all("", self._mouse_wheel_all, add="+") + self.bind_all("", self._keyboard_shift_press_all, add="+") + self.bind_all("", self._keyboard_shift_press_all, add="+") + self.bind_all("", self._keyboard_shift_release_all, add="+") + self.bind_all("", self._keyboard_shift_release_all, add="+") + self._create_window_id = self._parent_canvas.create_window(0, 0, window=self, anchor="nw") + + if self._parent_frame.cget("fg_color") == "transparent": + tkinter.Frame.configure(self, bg=self._apply_appearance_mode(self._parent_frame.cget("bg_color"))) + self._parent_canvas.configure(bg=self._apply_appearance_mode(self._parent_frame.cget("bg_color"))) + else: + tkinter.Frame.configure(self, bg=self._apply_appearance_mode(self._parent_frame.cget("fg_color"))) + self._parent_canvas.configure(bg=self._apply_appearance_mode(self._parent_frame.cget("fg_color"))) + + self._shift_pressed = False + + def destroy(self): + tkinter.Frame.destroy(self) + CTkAppearanceModeBaseClass.destroy(self) + CTkScalingBaseClass.destroy(self) + + def _create_grid(self): + border_spacing = self._apply_widget_scaling(self._parent_frame.cget("corner_radius") + self._parent_frame.cget("border_width")) + + if self._orientation == "horizontal": + self._parent_frame.grid_columnconfigure(0, weight=1) + self._parent_frame.grid_rowconfigure(1, weight=1) + self._parent_canvas.grid(row=1, column=0, sticky="nsew", padx=border_spacing, pady=(border_spacing, 0)) + self._scrollbar.grid(row=2, column=0, sticky="nsew", padx=border_spacing) + + if self._label_text is not None and self._label_text != "": + self._label.grid(row=0, column=0, sticky="ew", padx=border_spacing, pady=border_spacing) + else: + self._label.grid_forget() + + elif self._orientation == "vertical": + self._parent_frame.grid_columnconfigure(0, weight=1) + self._parent_frame.grid_rowconfigure(1, weight=1) + self._parent_canvas.grid(row=1, column=0, sticky="nsew", padx=(border_spacing, 0), pady=border_spacing) + self._scrollbar.grid(row=1, column=1, sticky="nsew", pady=border_spacing) + + if self._label_text is not None and self._label_text != "": + self._label.grid(row=0, column=0, columnspan=2, sticky="ew", padx=border_spacing, pady=border_spacing) + else: + self._label.grid_forget() + + def _set_appearance_mode(self, mode_string): + super()._set_appearance_mode(mode_string) + + if self._parent_frame.cget("fg_color") == "transparent": + tkinter.Frame.configure(self, bg=self._apply_appearance_mode(self._parent_frame.cget("bg_color"))) + self._parent_canvas.configure(bg=self._apply_appearance_mode(self._parent_frame.cget("bg_color"))) + else: + tkinter.Frame.configure(self, bg=self._apply_appearance_mode(self._parent_frame.cget("fg_color"))) + self._parent_canvas.configure(bg=self._apply_appearance_mode(self._parent_frame.cget("fg_color"))) + + def _set_scaling(self, new_widget_scaling, new_window_scaling): + super()._set_scaling(new_widget_scaling, new_window_scaling) + + self._parent_canvas.configure(width=self._apply_widget_scaling(self._desired_width), + height=self._apply_widget_scaling(self._desired_height)) + + def _set_dimensions(self, width=None, height=None): + if width is not None: + self._desired_width = width + if height is not None: + self._desired_height = height + + self._parent_canvas.configure(width=self._apply_widget_scaling(self._desired_width), + height=self._apply_widget_scaling(self._desired_height)) + + def configure(self, **kwargs): + if "width" in kwargs: + self._set_dimensions(width=kwargs.pop("width")) + + if "height" in kwargs: + self._set_dimensions(height=kwargs.pop("height")) + + if "corner_radius" in kwargs: + new_corner_radius = kwargs.pop("corner_radius") + self._parent_frame.configure(corner_radius=new_corner_radius) + if self._label is not None: + self._label.configure(corner_radius=new_corner_radius) + self._create_grid() + + if "border_width" in kwargs: + self._parent_frame.configure(border_width=kwargs.pop("border_width")) + self._create_grid() + + if "fg_color" in kwargs: + self._parent_frame.configure(fg_color=kwargs.pop("fg_color")) + + if self._parent_frame.cget("fg_color") == "transparent": + tkinter.Frame.configure(self, bg=self._apply_appearance_mode(self._parent_frame.cget("bg_color"))) + self._parent_canvas.configure(bg=self._apply_appearance_mode(self._parent_frame.cget("bg_color"))) + else: + tkinter.Frame.configure(self, bg=self._apply_appearance_mode(self._parent_frame.cget("fg_color"))) + self._parent_canvas.configure(bg=self._apply_appearance_mode(self._parent_frame.cget("fg_color"))) + + for child in self.winfo_children(): + if isinstance(child, CTkBaseClass): + child.configure(bg_color=self._parent_frame.cget("fg_color")) + + if "scrollbar_fg_color" in kwargs: + self._scrollbar.configure(fg_color=kwargs.pop("scrollbar_fg_color")) + + if "scrollbar_button_color" in kwargs: + self._scrollbar.configure(button_color=kwargs.pop("scrollbar_button_color")) + + if "scrollbar_button_hover_color" in kwargs: + self._scrollbar.configure(button_hover_color=kwargs.pop("scrollbar_button_hover_color")) + + if "label_text" in kwargs: + self._label_text = kwargs.pop("label_text") + self._label.configure(text=self._label_text) + self._create_grid() + + if "label_font" in kwargs: + self._label.configure(font=kwargs.pop("label_font")) + + if "label_text_color" in kwargs: + self._label.configure(text_color=kwargs.pop("label_text_color")) + + if "label_fg_color" in kwargs: + self._label.configure(fg_color=kwargs.pop("label_fg_color")) + + if "label_anchor" in kwargs: + self._label.configure(anchor=kwargs.pop("label_anchor")) + + self._parent_frame.configure(**kwargs) + + def cget(self, attribute_name: str): + if attribute_name == "width": + return self._desired_width + elif attribute_name == "height": + return self._desired_height + + elif attribute_name == "label_text": + return self._label_text + elif attribute_name == "label_font": + return self._label.cget("font") + elif attribute_name == "label_text_color": + return self._label.cget("_text_color") + elif attribute_name == "label_fg_color": + return self._label.cget("fg_color") + elif attribute_name == "label_anchor": + return self._label.cget("anchor") + + elif attribute_name.startswith("scrollbar_fg_color"): + return self._scrollbar.cget("fg_color") + elif attribute_name.startswith("scrollbar_button_color"): + return self._scrollbar.cget("button_color") + elif attribute_name.startswith("scrollbar_button_hover_color"): + return self._scrollbar.cget("button_hover_color") + + else: + return self._parent_frame.cget(attribute_name) + + def _fit_frame_dimensions_to_canvas(self, event): + if self._orientation == "horizontal": + self._parent_canvas.itemconfigure(self._create_window_id, height=self._parent_canvas.winfo_height()) + elif self._orientation == "vertical": + self._parent_canvas.itemconfigure(self._create_window_id, width=self._parent_canvas.winfo_width()) + + def _set_scroll_increments(self): + if sys.platform.startswith("win"): + self._parent_canvas.configure(xscrollincrement=1, yscrollincrement=1) + elif sys.platform == "darwin": + self._parent_canvas.configure(xscrollincrement=4, yscrollincrement=8) + + def _mouse_wheel_all(self, event): + if self.check_if_master_is_canvas(event.widget): + if sys.platform.startswith("win"): + if self._shift_pressed: + if self._parent_canvas.xview() != (0.0, 1.0): + self._parent_canvas.xview("scroll", -int(event.delta / 6), "units") + else: + if self._parent_canvas.yview() != (0.0, 1.0): + self._parent_canvas.yview("scroll", -int(event.delta / 6), "units") + elif sys.platform == "darwin": + if self._shift_pressed: + if self._parent_canvas.xview() != (0.0, 1.0): + self._parent_canvas.xview("scroll", -event.delta, "units") + else: + if self._parent_canvas.yview() != (0.0, 1.0): + self._parent_canvas.yview("scroll", -event.delta, "units") + else: + if self._shift_pressed: + if self._parent_canvas.xview() != (0.0, 1.0): + self._parent_canvas.xview("scroll", -event.delta, "units") + else: + if self._parent_canvas.yview() != (0.0, 1.0): + self._parent_canvas.yview("scroll", -event.delta, "units") + + def _keyboard_shift_press_all(self, event): + self._shift_pressed = True + + def _keyboard_shift_release_all(self, event): + self._shift_pressed = False + + def check_if_master_is_canvas(self, widget): + if widget == self._parent_canvas: + return True + elif widget.master is not None: + return self.check_if_master_is_canvas(widget.master) + else: + return False + + def pack(self, **kwargs): + self._parent_frame.pack(**kwargs) + + def place(self, **kwargs): + self._parent_frame.place(**kwargs) + + def grid(self, **kwargs): + self._parent_frame.grid(**kwargs) + + def pack_forget(self): + self._parent_frame.pack_forget() + + def place_forget(self, **kwargs): + self._parent_frame.place_forget() + + def grid_forget(self, **kwargs): + self._parent_frame.grid_forget() + + def grid_remove(self, **kwargs): + self._parent_frame.grid_remove() + + def grid_propagate(self, **kwargs): + self._parent_frame.grid_propagate() + + def grid_info(self, **kwargs): + return self._parent_frame.grid_info() + + def lift(self, aboveThis=None): + self._parent_frame.lift(aboveThis) + + def lower(self, belowThis=None): + self._parent_frame.lower(belowThis) From 728bbf78acd465fba993da7eaac1a354e96a8b1a Mon Sep 17 00:00:00 2001 From: LYMwyh <122608577+LYMwyh@users.noreply.github.com> Date: Sun, 1 Sep 2024 15:09:54 +0800 Subject: [PATCH 7/7] update ctk_scrollable_frame.py --- .../windows/widgets/ctk_scrollable_frame.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/customtkinter/windows/widgets/ctk_scrollable_frame.py b/customtkinter/windows/widgets/ctk_scrollable_frame.py index eede0911..0a67ed51 100644 --- a/customtkinter/windows/widgets/ctk_scrollable_frame.py +++ b/customtkinter/windows/widgets/ctk_scrollable_frame.py @@ -72,7 +72,7 @@ def __init__(self, self._parent_canvas.configure(width=self._apply_widget_scaling(self._desired_width), height=self._apply_widget_scaling(self._desired_height)) - self.bind("", lambda e: self._parent_canvas.configure(scrollregion=self._parent_canvas.bbox("all"))) + self.bind("", self._update_scroll_region) self._parent_canvas.bind("", self._fit_frame_dimensions_to_canvas) self.bind_all("", self._mouse_wheel_all, add="+") self.bind_all("", self._keyboard_shift_press_all, add="+") @@ -238,6 +238,23 @@ def _fit_frame_dimensions_to_canvas(self, event): elif self._orientation == "vertical": self._parent_canvas.itemconfigure(self._create_window_id, width=self._parent_canvas.winfo_width()) + self._check_scroll_necessity() + + def _check_scroll_necessity(self): + canvas_height = self._parent_canvas.winfo_height() + content_height = self.winfo_height() + + if content_height <= canvas_height: + self._scrollbar.grid_remove() + self._parent_canvas.configure(yscrollcommand=None) + else: + self._scrollbar.grid() + self._parent_canvas.configure(yscrollcommand=self._scrollbar.set) + + def _update_scroll_region(self, event=None): + self._parent_canvas.configure(scrollregion=self._parent_canvas.bbox("all")) + self._check_scroll_necessity() + def _set_scroll_increments(self): if sys.platform.startswith("win"): self._parent_canvas.configure(xscrollincrement=1, yscrollincrement=1)