added CTkScrollbar

This commit is contained in:
Tom Schimansky 2022-06-19 21:16:19 +02:00
parent 3b259e4d01
commit 79ecd2e946
6 changed files with 403 additions and 18 deletions

View File

@ -53,6 +53,7 @@ from .widgets.ctk_canvas import CTkCanvas
from .widgets.ctk_switch import CTkSwitch
from .widgets.ctk_optionmenu import CTkOptionMenu
from .widgets.ctk_combobox import CTkComboBox
from .widgets.ctk_scrollbar import CTkScrollbar
# import windows
from .windows.ctk_tk import CTk

View File

@ -33,7 +33,9 @@
"combobox_button_hover": ["#6E7174", "#7A848D"],
"dropdown_color": ["gray90", "gray20"],
"dropdown_hover": ["gray75", "gray28"],
"dropdown_text": ["gray10", "#DCE4EE"]
"dropdown_text": ["gray10", "#DCE4EE"],
"scrollbar": ["gray75", "gray25"],
"scrollbar_hover": ["gray50", "gray40"]
},
"text": {
"macOS": {
@ -70,6 +72,8 @@
"switch_border_width": 3,
"switch_corner_radius": 1000,
"switch_button_corner_radius": 1000,
"switch_button_length": 0
"switch_button_length": 0,
"scrollbar_corner_radius": 6,
"scrollbar_border_spacing": 4
}
}

View File

@ -20,6 +20,7 @@ class DrawEngine:
- draw_rounded_rect_with_border_vertical_split()
- draw_rounded_progress_bar_with_border()
- draw_rounded_slider_with_border_and_button()
- draw_rounded_scrollbar()
- draw_checkmark()
- draw_dropdown_arrow()
@ -38,7 +39,7 @@ class DrawEngine:
else:
return round(user_corner_radius)
# optimize forx drawing with antialiased font shapes
# optimize for drawing with antialiased font shapes
elif self.preferred_drawing_method == "font_shapes":
return round(user_corner_radius)
@ -490,22 +491,27 @@ class DrawEngine:
# create canvas border corner parts if not already created, but only if needed, and delete if not needed
if not self._canvas.find_withtag("border_oval_1_a") and "border_oval_1" not in exclude_parts:
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_1_a", "border_corner_part", "border_parts_left", "border_parts", "left_parts"), anchor=tkinter.CENTER)
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_1_b", "border_corner_part", "border_parts_left", "border_parts", "left_parts"), anchor=tkinter.CENTER, angle=180)
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_1_b", "border_corner_part", "border_parts_left", "border_parts", "left_parts"), anchor=tkinter.CENTER,
angle=180)
requires_recoloring = True
elif self._canvas.find_withtag("border_oval_1_a") and "border_oval_1" in exclude_parts:
self._canvas.delete("border_oval_1_a", "border_oval_1_b")
if not self._canvas.find_withtag("border_oval_2_a") and width > 2 * corner_radius and "border_oval_2" not in exclude_parts:
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_2_a", "border_corner_part", "border_parts_right", "border_parts", "right_parts"), anchor=tkinter.CENTER)
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_2_b", "border_corner_part", "border_parts_right", "border_parts", "right_parts"), anchor=tkinter.CENTER, angle=180)
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_2_a", "border_corner_part", "border_parts_right", "border_parts", "right_parts"),
anchor=tkinter.CENTER)
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_2_b", "border_corner_part", "border_parts_right", "border_parts", "right_parts"),
anchor=tkinter.CENTER, angle=180)
requires_recoloring = True
elif self._canvas.find_withtag("border_oval_2_a") and (not width > 2 * corner_radius or "border_oval_2" in exclude_parts):
self._canvas.delete("border_oval_2_a", "border_oval_2_b")
if not self._canvas.find_withtag("border_oval_3_a") and height > 2 * corner_radius \
and width > 2 * corner_radius and "border_oval_3" not in exclude_parts:
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_3_a", "border_corner_part", "border_parts_right", "border_parts", "right_parts"), anchor=tkinter.CENTER)
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_3_b", "border_corner_part", "border_parts_right", "border_parts", "right_parts"), anchor=tkinter.CENTER, angle=180)
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_3_a", "border_corner_part", "border_parts_right", "border_parts", "right_parts"),
anchor=tkinter.CENTER)
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_3_b", "border_corner_part", "border_parts_right", "border_parts", "right_parts"),
anchor=tkinter.CENTER, angle=180)
requires_recoloring = True
elif self._canvas.find_withtag("border_oval_3_a") and (not (height > 2 * corner_radius
and width > 2 * corner_radius) or "border_oval_3" in exclude_parts):
@ -513,7 +519,8 @@ class DrawEngine:
if not self._canvas.find_withtag("border_oval_4_a") and height > 2 * corner_radius and "border_oval_4" not in exclude_parts:
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_4_a", "border_corner_part", "border_parts_left", "border_parts", "left_parts"), anchor=tkinter.CENTER)
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_4_b", "border_corner_part", "border_parts_left", "border_parts", "left_parts"), anchor=tkinter.CENTER, angle=180)
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_4_b", "border_corner_part", "border_parts_left", "border_parts", "left_parts"), anchor=tkinter.CENTER,
angle=180)
requires_recoloring = True
elif self._canvas.find_withtag("border_oval_4_a") and (not height > 2 * corner_radius or "border_oval_4" in exclude_parts):
self._canvas.delete("border_oval_4_a", "border_oval_4_b")
@ -554,14 +561,16 @@ class DrawEngine:
# create canvas border corner parts if not already created, but only if they're needed and delete if not needed
if not self._canvas.find_withtag("inner_oval_1_a") and "inner_oval_1" not in exclude_parts:
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_1_a", "inner_corner_part", "inner_parts_left", "inner_parts", "left_parts"), anchor=tkinter.CENTER)
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_1_b", "inner_corner_part", "inner_parts_left", "inner_parts", "left_parts"), anchor=tkinter.CENTER, angle=180)
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_1_b", "inner_corner_part", "inner_parts_left", "inner_parts", "left_parts"), anchor=tkinter.CENTER,
angle=180)
requires_recoloring = True
elif self._canvas.find_withtag("inner_oval_1_a") and "inner_oval_1" in exclude_parts:
self._canvas.delete("inner_oval_1_a", "inner_oval_1_b")
if not self._canvas.find_withtag("inner_oval_2_a") and width - (2 * border_width) > 2 * inner_corner_radius and "inner_oval_2" not in exclude_parts:
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_2_a", "inner_corner_part","inner_parts_right", "inner_parts", "right_parts"), anchor=tkinter.CENTER)
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_2_b", "inner_corner_part", "inner_parts_right", "inner_parts", "right_parts"), anchor=tkinter.CENTER, angle=180)
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_2_a", "inner_corner_part", "inner_parts_right", "inner_parts", "right_parts"), anchor=tkinter.CENTER)
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_2_b", "inner_corner_part", "inner_parts_right", "inner_parts", "right_parts"), anchor=tkinter.CENTER,
angle=180)
requires_recoloring = True
elif self._canvas.find_withtag("inner_oval_2_a") and (not width - (2 * border_width) > 2 * inner_corner_radius or "inner_oval_2" in exclude_parts):
self._canvas.delete("inner_oval_2_a", "inner_oval_2_b")
@ -569,7 +578,8 @@ class DrawEngine:
if not self._canvas.find_withtag("inner_oval_3_a") and height - (2 * border_width) > 2 * inner_corner_radius \
and width - (2 * border_width) > 2 * inner_corner_radius and "inner_oval_3" not in exclude_parts:
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_3_a", "inner_corner_part", "inner_parts_right", "inner_parts", "right_parts"), anchor=tkinter.CENTER)
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_3_b", "inner_corner_part", "inner_parts_right", "inner_parts", "right_parts"), anchor=tkinter.CENTER, angle=180)
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_3_b", "inner_corner_part", "inner_parts_right", "inner_parts", "right_parts"), anchor=tkinter.CENTER,
angle=180)
requires_recoloring = True
elif self._canvas.find_withtag("inner_oval_3_a") and (not (height - (2 * border_width) > 2 * inner_corner_radius
and width - (2 * border_width) > 2 * inner_corner_radius) or "inner_oval_3" in exclude_parts):
@ -577,7 +587,8 @@ class DrawEngine:
if not self._canvas.find_withtag("inner_oval_4_a") and height - (2 * border_width) > 2 * inner_corner_radius and "inner_oval_4" not in exclude_parts:
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_4_a", "inner_corner_part", "inner_parts_left", "inner_parts", "left_parts"), anchor=tkinter.CENTER)
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_4_b", "inner_corner_part", "inner_parts_left", "inner_parts", "left_parts"), anchor=tkinter.CENTER, angle=180)
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_4_b", "inner_corner_part", "inner_parts_left", "inner_parts", "left_parts"), anchor=tkinter.CENTER,
angle=180)
requires_recoloring = True
elif self._canvas.find_withtag("inner_oval_4_a") and (not height - (2 * border_width) > 2 * inner_corner_radius or "inner_oval_4" in exclude_parts):
self._canvas.delete("inner_oval_4_a", "inner_oval_4_b")
@ -959,6 +970,138 @@ class DrawEngine:
return requires_recoloring
def draw_rounded_scrollbar(self, width: Union[float, int], height: Union[float, int], corner_radius: Union[float, int],
border_spacing: Union[float, int], start_value: float, end_value: float, orientation: str) -> bool:
width = math.floor(width / 2) * 2 # round _current_width and _current_height and restrict them to even values only
height = math.floor(height / 2) * 2
if corner_radius > width / 2 or corner_radius > height / 2: # restrict corner_radius if it's too larger
corner_radius = min(width / 2, height / 2)
border_spacing = round(border_spacing)
corner_radius = self.__calc_optimal_corner_radius(corner_radius) # optimize corner_radius for different drawing methods (different rounding)
if corner_radius >= border_spacing:
inner_corner_radius = corner_radius - border_spacing
else:
inner_corner_radius = 0
if self.preferred_drawing_method == "polygon_shapes" or self.preferred_drawing_method == "circle_shapes":
return self.__draw_rounded_scrollbar_polygon_shapes(width, height, corner_radius, inner_corner_radius,
start_value, end_value, orientation)
elif self.preferred_drawing_method == "font_shapes":
return self.__draw_rounded_scrollbar_font_shapes(width, height, corner_radius, inner_corner_radius,
start_value, end_value, orientation)
def __draw_rounded_scrollbar_polygon_shapes(self, width: int, height: int, corner_radius: int, inner_corner_radius: int,
start_value: float, end_value: float, orientation: str) -> bool:
requires_recoloring = False
if not self._canvas.find_withtag("border_parts"):
self._canvas.create_rectangle(0, 0, 0, 0, tags=("border_rectangle_1", "border_parts"), width=0)
requires_recoloring = True
self._canvas.coords("border_rectangle_1", 0, 0, width, height)
if not self._canvas.find_withtag("scrollbar_parts"):
self._canvas.create_polygon((0, 0, 0, 0), tags=("scrollbar_polygon_1", "scrollbar_parts"), joinstyle=tkinter.ROUND)
self._canvas.tag_raise("scrollbar_parts", "border_parts")
requires_recoloring = True
if orientation == "vertical":
self._canvas.coords("scrollbar_polygon_1",
corner_radius, corner_radius + (height - 2 * corner_radius) * start_value,
width - corner_radius, corner_radius + (height - 2 * corner_radius) * start_value,
width - corner_radius, corner_radius + (height - 2 * corner_radius) * end_value,
corner_radius, corner_radius + (height - 2 * corner_radius) * end_value)
elif orientation == "horizontal":
self._canvas.coords("scrollbar_polygon_1",
corner_radius + (width - 2 * corner_radius) * start_value, corner_radius,
corner_radius + (width - 2 * corner_radius) * end_value, corner_radius,
corner_radius + (width - 2 * corner_radius) * end_value, height - corner_radius,
corner_radius + (width - 2 * corner_radius) * start_value, height - corner_radius,)
self._canvas.itemconfig("scrollbar_polygon_1", width=inner_corner_radius * 2)
return requires_recoloring
def __draw_rounded_scrollbar_font_shapes(self, width: int, height: int, corner_radius: int, inner_corner_radius: int,
start_value: float, end_value: float, orientation: str) -> bool:
requires_recoloring = False
if not self._canvas.find_withtag("border_parts"):
self._canvas.create_rectangle(0, 0, 0, 0, tags=("border_rectangle_1", "border_parts"), width=0)
requires_recoloring = True
self._canvas.coords("border_rectangle_1", 0, 0, width, height)
if inner_corner_radius > 0:
if not self._canvas.find_withtag("scrollbar_oval_1_a"):
self._canvas.create_aa_circle(0, 0, 0, tags=("scrollbar_oval_1_a", "scrollbar_corner_part", "scrollbar_parts"), anchor=tkinter.CENTER)
self._canvas.create_aa_circle(0, 0, 0, tags=("scrollbar_oval_1_b", "scrollbar_corner_part", "scrollbar_parts"), anchor=tkinter.CENTER, angle=180)
requires_recoloring = True
if not self._canvas.find_withtag("scrollbar_oval_2_a") and width > 2 * corner_radius:
self._canvas.create_aa_circle(0, 0, 0, tags=("scrollbar_oval_2_a", "scrollbar_corner_part", "scrollbar_parts"), anchor=tkinter.CENTER)
self._canvas.create_aa_circle(0, 0, 0, tags=("scrollbar_oval_2_b", "scrollbar_corner_part", "scrollbar_parts"), anchor=tkinter.CENTER, angle=180)
requires_recoloring = True
elif self._canvas.find_withtag("scrollbar_oval_2_a") and not width > 2 * corner_radius:
self._canvas.delete("scrollbar_oval_2_a", "scrollbar_oval_2_b")
if not self._canvas.find_withtag("scrollbar_oval_3_a") and height > 2 * corner_radius and width > 2 * corner_radius:
self._canvas.create_aa_circle(0, 0, 0, tags=("scrollbar_oval_3_a", "scrollbar_corner_part", "scrollbar_parts"), anchor=tkinter.CENTER)
self._canvas.create_aa_circle(0, 0, 0, tags=("scrollbar_oval_3_b", "scrollbar_corner_part", "scrollbar_parts"), anchor=tkinter.CENTER, angle=180)
requires_recoloring = True
elif self._canvas.find_withtag("scrollbar_oval_3_a") and not (height > 2 * corner_radius and width > 2 * corner_radius):
self._canvas.delete("scrollbar_oval_3_a", "scrollbar_oval_3_b")
if not self._canvas.find_withtag("scrollbar_oval_4_a") and height > 2 * corner_radius:
self._canvas.create_aa_circle(0, 0, 0, tags=("scrollbar_oval_4_a", "scrollbar_corner_part", "scrollbar_parts"), anchor=tkinter.CENTER)
self._canvas.create_aa_circle(0, 0, 0, tags=("scrollbar_oval_4_b", "scrollbar_corner_part", "scrollbar_parts"), anchor=tkinter.CENTER, angle=180)
requires_recoloring = True
elif self._canvas.find_withtag("scrollbar_oval_4_a") and not height > 2 * corner_radius:
self._canvas.delete("scrollbar_oval_4_a", "scrollbar_oval_4_b")
else:
self._canvas.delete("scrollbar_corner_part")
if not self._canvas.find_withtag("scrollbar_rectangle_1") and width > 2 * corner_radius:
self._canvas.create_rectangle(0, 0, 0, 0, tags=("scrollbar_rectangle_1", "scrollbar_rectangle_part", "scrollbar_parts"), width=0)
requires_recoloring = True
elif self._canvas.find_withtag("scrollbar_rectangle_1") and not width > 2 * corner_radius:
self._canvas.delete("scrollbar_rectangle_1")
if not self._canvas.find_withtag("scrollbar_rectangle_2"):
self._canvas.create_rectangle(0, 0, 0, 0, tags=("scrollbar_rectangle_2", "scrollbar_rectangle_part", "scrollbar_parts"), width=0)
requires_recoloring = True
elif self._canvas.find_withtag("scrollbar_rectangle_2") and not height > 2 * corner_radius:
self._canvas.delete("scrollbar_rectangle_2")
self._canvas.coords("scrollbar_rectangle_1",
corner_radius - inner_corner_radius, corner_radius + (height - 2 * corner_radius) * start_value,
width - (corner_radius - inner_corner_radius), corner_radius + (height - 2 * corner_radius) * end_value)
self._canvas.coords("scrollbar_rectangle_2",
corner_radius, corner_radius - inner_corner_radius + (height - 2 * corner_radius) * start_value,
width - (corner_radius), corner_radius + inner_corner_radius + (height - 2 * corner_radius) * end_value)
if orientation == "vertical":
self._canvas.coords("scrollbar_oval_1_a", corner_radius, corner_radius + (height - 2 * corner_radius) * start_value, inner_corner_radius)
self._canvas.coords("scrollbar_oval_1_b", corner_radius, corner_radius + (height - 2 * corner_radius) * start_value, inner_corner_radius)
self._canvas.coords("scrollbar_oval_2_a", width - corner_radius, corner_radius + (height - 2 * corner_radius) * start_value, inner_corner_radius)
self._canvas.coords("scrollbar_oval_2_b", width - corner_radius, corner_radius + (height - 2 * corner_radius) * start_value, inner_corner_radius)
self._canvas.coords("scrollbar_oval_3_a", width - corner_radius, corner_radius + (height - 2 * corner_radius) * end_value, inner_corner_radius)
self._canvas.coords("scrollbar_oval_3_b", width - corner_radius, corner_radius + (height - 2 * corner_radius) * end_value, inner_corner_radius)
self._canvas.coords("scrollbar_oval_4_a", corner_radius, corner_radius + (height - 2 * corner_radius) * end_value, inner_corner_radius)
self._canvas.coords("scrollbar_oval_4_b", corner_radius, corner_radius + (height - 2 * corner_radius) * end_value, inner_corner_radius)
if orientation == "horizontal":
self._canvas.coords("scrollbar_oval_1_a", corner_radius + (width - 2 * corner_radius) * start_value, corner_radius, inner_corner_radius)
self._canvas.coords("scrollbar_oval_1_b", corner_radius + (width - 2 * corner_radius) * start_value, corner_radius, inner_corner_radius)
self._canvas.coords("scrollbar_oval_2_a", corner_radius + (width - 2 * corner_radius) * end_value, corner_radius, inner_corner_radius)
self._canvas.coords("scrollbar_oval_2_b", corner_radius + (width - 2 * corner_radius) * end_value, corner_radius, inner_corner_radius)
self._canvas.coords("scrollbar_oval_3_a", corner_radius + (width - 2 * corner_radius) * end_value, height - corner_radius, inner_corner_radius)
self._canvas.coords("scrollbar_oval_3_b", corner_radius + (width - 2 * corner_radius) * end_value, height - corner_radius, inner_corner_radius)
self._canvas.coords("scrollbar_oval_4_a", corner_radius + (width - 2 * corner_radius) * start_value, height - corner_radius, inner_corner_radius)
self._canvas.coords("scrollbar_oval_4_b", corner_radius + (width - 2 * corner_radius) * start_value, height - corner_radius, inner_corner_radius)
return requires_recoloring
def draw_checkmark(self, width: Union[float, int], height: Union[float, int], size: Union[int, float]) -> bool:
""" Draws a rounded rectangle with a corner_radius and border_width on the canvas. The border elements have a 'border_parts' tag,
the main foreground elements have an 'inner_parts' tag to color the elements accordingly.

View File

@ -0,0 +1,200 @@
from .ctk_canvas import CTkCanvas
from ..theme_manager import ThemeManager
from ..draw_engine import DrawEngine
from .widget_base_class import CTkBaseClass
class CTkScrollbar(CTkBaseClass):
def __init__(self, *args,
bg_color=None,
fg_color=None,
scrollbar_color="default_theme",
scrollbar_hover_color="default_theme",
border_spacing="default_theme",
corner_radius="default_theme",
width=None,
height=None,
orientation="vertical",
command=None,
hover=True,
**kwargs):
# set default dimensions according to orientation
if width is None:
if orientation.lower() == "vertical":
width = 16
else:
width = 200
if height is None:
if orientation.lower() == "vertical":
height = 200
else:
height = 16
# transfer basic functionality (bg_color, size, _appearance_mode, scaling) to CTkBaseClass
super().__init__(*args, bg_color=bg_color, width=width, height=height, **kwargs)
# color
self.fg_color = fg_color
self.scrollbar_color = ThemeManager.theme["color"]["scrollbar"] if scrollbar_color == "default_theme" else scrollbar_color
self.scrollbar_hover_color = ThemeManager.theme["color"]["scrollbar_hover"] if scrollbar_hover_color == "default_theme" else scrollbar_hover_color
# shape
self.corner_radius = ThemeManager.theme["shape"]["scrollbar_corner_radius"] if corner_radius == "default_theme" else corner_radius
self.border_spacing = ThemeManager.theme["shape"]["scrollbar_border_spacing"] if border_spacing == "default_theme" else border_spacing
self.hover = hover
self.hover_state = False
self.command = command
self.orientation = orientation
self.start_value: float = 0 # 0 to 1
self.end_value: float = 1 # 0 to 1
self.canvas = CTkCanvas(master=self,
highlightthickness=0,
width=self.apply_widget_scaling(self._current_width),
height=self.apply_widget_scaling(self._current_height))
self.canvas.place(x=0, y=0, relwidth=1, relheight=1)
self.canvas.configure(bg="green")
self.draw_engine = DrawEngine(self.canvas)
self.canvas.bind("<Enter>", self.on_enter)
self.canvas.bind("<Leave>", self.on_leave)
self.canvas.tag_bind("border_parts", "<Button-1>", self.clicked)
self.canvas.bind("<B1-Motion>", self.clicked)
self.canvas.bind("<MouseWheel>", self.mouse_scroll_event)
self.bind('<Configure>', self.update_dimensions_event)
self.draw()
def set_scaling(self, *args, **kwargs):
super().set_scaling(*args, **kwargs)
self.canvas.configure(width=self.apply_widget_scaling(self._desired_width), height=self.apply_widget_scaling(self._desired_height))
self.draw(no_color_updates=True)
def set_dimensions(self, width=None, height=None):
super().set_dimensions(width, height)
self.canvas.configure(width=self.apply_widget_scaling(self._desired_width),
height=self.apply_widget_scaling(self._desired_height))
self.draw(no_color_updates=True)
def draw(self, no_color_updates=False):
requires_recoloring = self.draw_engine.draw_rounded_scrollbar(self.apply_widget_scaling(self._current_width),
self.apply_widget_scaling(self._current_height),
self.apply_widget_scaling(self.corner_radius),
self.apply_widget_scaling(self.border_spacing),
self.start_value, self.end_value,
self.orientation)
if no_color_updates is False or requires_recoloring:
if self.hover_state is True:
self.canvas.itemconfig("scrollbar_parts",
fill=ThemeManager.single_color(self.scrollbar_hover_color, self._appearance_mode),
outline=ThemeManager.single_color(self.scrollbar_hover_color, self._appearance_mode))
else:
self.canvas.itemconfig("scrollbar_parts",
fill=ThemeManager.single_color(self.scrollbar_color, self._appearance_mode),
outline=ThemeManager.single_color(self.scrollbar_color, self._appearance_mode))
if self.fg_color is None:
self.canvas.itemconfig("border_parts",
fill=ThemeManager.single_color(self.bg_color, self._appearance_mode),
outline=ThemeManager.single_color(self.bg_color, self._appearance_mode))
else:
self.canvas.itemconfig("border_parts",
fill=ThemeManager.single_color(self.fg_color, self._appearance_mode),
outline=ThemeManager.single_color(self.fg_color, self._appearance_mode))
def set(self, start_value: float, end_value: float):
self.start_value = float(start_value)
self.end_value = float(end_value)
self.scrollbar_height = self.end_value - self.start_value
self.draw()
def get(self):
return self.start_value, self.end_value
def configure(self, *args, **kwargs):
require_redraw = False # some attribute changes require a call of self.draw() at the end
if "bg_color" in kwargs:
if kwargs["bg_color"] is None:
self.bg_color = self.detect_color_of_master()
else:
self.bg_color = kwargs["bg_color"]
require_redraw = True
del kwargs["bg_color"]
if "fg_color" in kwargs:
self.fg_color = kwargs["fg_color"]
require_redraw = True
del kwargs["fg_color"]
if "scrollbar_color" in kwargs:
self.scrollbar_color = kwargs["scrollbar_color"]
require_redraw = True
del kwargs["scrollbar_color"]
if "scrollbar_hover_color" in kwargs:
self.scrollbar_hover_color = kwargs["scrollbar_hover_color"]
require_redraw = True
del kwargs["scrollbar_hover_color"]
if "command" in kwargs:
self.command = kwargs["command"]
del kwargs["command"]
if "corner_radius" in kwargs:
self.corner_radius = kwargs["corner_radius"]
require_redraw = True
del kwargs["corner_radius"]
if "border_spacing" in kwargs:
self.border_spacing = kwargs["border_spacing"]
require_redraw = True
del kwargs["border_spacing"]
if "width" in kwargs:
self.set_dimensions(width=kwargs["width"])
del kwargs["width"]
if "height" in kwargs:
self.set_dimensions(height=kwargs["height"])
del kwargs["height"]
super().configure(*args, **kwargs)
if require_redraw:
self.draw()
def on_enter(self, event=0):
if self.hover is True:
self.hover_state = True
self.canvas.itemconfig("scrollbar_parts",
outline=ThemeManager.single_color(self.scrollbar_hover_color, self._appearance_mode),
fill=ThemeManager.single_color(self.scrollbar_hover_color, self._appearance_mode))
def on_leave(self, event=0):
self.hover_state = False
self.canvas.itemconfig("scrollbar_parts",
outline=ThemeManager.single_color(self.scrollbar_color, self._appearance_mode),
fill=ThemeManager.single_color(self.scrollbar_color, self._appearance_mode))
def clicked(self, event):
if self.orientation == "vertical":
value = ((event.y - self.border_spacing) / (self._current_height - 2 * self.border_spacing)) / self._widget_scaling
current_scrollbar_length = self.end_value - self.start_value
value = max(current_scrollbar_length / 2, min(value, 1 - (current_scrollbar_length / 2)))
self.start_value = value - (current_scrollbar_length / 2)
self.end_value = value + (current_scrollbar_length / 2)
self.draw()
if self.command is not None:
self.command('moveto', self.start_value)
def mouse_scroll_event(self, event=None):
if self.command is not None:
self.command('scroll', event.delta, 'units')

View File

@ -61,7 +61,7 @@ class CTkSlider(CTkBaseClass):
self.border_width = ThemeManager.theme["shape"]["slider_border_width"] if border_width == "default_theme" else border_width
self.button_length = ThemeManager.theme["shape"]["slider_button_length"] if button_length == "default_theme" else button_length
self.value = 0.5 # initial value of slider in percent
self.orient = orient
self.orientation = orient
self.hover_state = False
self.from_ = from_
self.to = to
@ -139,9 +139,9 @@ class CTkSlider(CTkBaseClass):
self.configure(cursor="arrow")
def draw(self, no_color_updates=False):
if self.orient.lower() == "horizontal":
if self.orientation.lower() == "horizontal":
orientation = "w"
elif self.orient.lower() == "vertical":
elif self.orientation.lower() == "vertical":
orientation = "s"
else:
orientation = "w"
@ -185,7 +185,7 @@ class CTkSlider(CTkBaseClass):
def clicked(self, event=None):
if self.state == "normal":
if self.orient.lower() == "horizontal":
if self.orientation.lower() == "horizontal":
self.value = (event.x / self._current_width) / self._widget_scaling
else:
self.value = 1 - (event.y / self._current_height) / self._widget_scaling

View File

@ -0,0 +1,37 @@
import tkinter
import customtkinter
# customtkinter.DrawEngine.preferred_drawing_method = "font_shapes"
def to_scollbar(*args, **kwargs):
tk_textbox_scrollbar.set(*args, **kwargs)
ctk_textbox_scrollbar.set(*args, **kwargs)
ctk_textbox_scrollbar.update_idletasks()
tk_textbox_scrollbar.update_idletasks()
print("to_scollbar:", args, **kwargs)
def from_scrollbar(*args, **kwargs):
tk_textbox.yview(*args, **kwargs)
print("from_scrollbar:", args, **kwargs)
app = customtkinter.CTk()
app.grid_rowconfigure(0, weight=1)
app.grid_columnconfigure(0, weight=1)
tk_textbox = tkinter.Text(app)
tk_textbox.grid(row=0, column=0, sticky="nsew")
ctk_textbox_scrollbar = customtkinter.CTkScrollbar(app, command=from_scrollbar, fg_color="red")
ctk_textbox_scrollbar.grid(row=0, column=1, padx=0, sticky="ns")
tk_textbox_scrollbar = tkinter.Scrollbar(app, command=from_scrollbar)
tk_textbox_scrollbar.grid(row=0, column=2, padx=1, sticky="ns")
tk_textbox.configure(yscrollcommand=to_scollbar)
tk_textbox.insert("insert", "\n".join([str(i) for i in range(100)]))
app.mainloop()