finished CTkSegmentedButton, created test_segmented_button.py

This commit is contained in:
Tom Schimansky 2022-10-08 01:50:09 +02:00
parent 1696016d54
commit 327957e97a
12 changed files with 571 additions and 33 deletions

View File

@ -65,6 +65,7 @@ from .widgets.ctk_combobox import CTkComboBox
from .widgets.ctk_scrollbar import CTkScrollbar from .widgets.ctk_scrollbar import CTkScrollbar
from .widgets.ctk_textbox import CTkTextbox from .widgets.ctk_textbox import CTkTextbox
from .widgets.ctk_tabview import CTkTabview as _CTkTabview from .widgets.ctk_tabview import CTkTabview as _CTkTabview
from .widgets.ctk_segmented_button import CTkSegmentedButton as _CTkSegmentedButton
# import windows # import windows
from .windows.ctk_tk import CTk from .windows.ctk_tk import CTk

View File

@ -5,7 +5,7 @@
"button_hover": ["#36719F", "#144870"], "button_hover": ["#36719F", "#144870"],
"button_border": ["#3E454A", "#949A9F"], "button_border": ["#3E454A", "#949A9F"],
"checkbox_border": ["#3E454A", "#949A9F"], "checkbox_border": ["#3E454A", "#949A9F"],
"checkmark": ["white", "gray90"], "checkmark": ["#DCE4EE", "gray90"],
"entry": ["#F9F9FA", "#343638"], "entry": ["#F9F9FA", "#343638"],
"entry_border": ["#979DA2", "#565B5E"], "entry_border": ["#979DA2", "#565B5E"],
"entry_placeholder_text": ["gray52", "gray62"], "entry_placeholder_text": ["gray52", "gray62"],
@ -15,7 +15,8 @@
"label": [null, null], "label": [null, null],
"text": ["gray10", "#DCE4EE"], "text": ["gray10", "#DCE4EE"],
"text_disabled": ["gray60", "#777B80"], "text_disabled": ["gray60", "#777B80"],
"text_button_disabled": ["gray40", "gray74"], "text_button": ["#DCE4EE", "#DCE4EE"],
"text_button_disabled": ["gray74", "gray74"],
"progressbar": ["#939BA2", "#4A4D50"], "progressbar": ["#939BA2", "#4A4D50"],
"progressbar_progress": ["#3B8ED0", "#1F6AA5"], "progressbar_progress": ["#3B8ED0", "#1F6AA5"],
"progressbar_border": ["gray", "gray"], "progressbar_border": ["gray", "gray"],
@ -36,6 +37,9 @@
"dropdown_text": ["gray10", "#DCE4EE"], "dropdown_text": ["gray10", "#DCE4EE"],
"scrollbar_button": ["gray55", "gray41"], "scrollbar_button": ["gray55", "gray41"],
"scrollbar_button_hover": ["gray40", "gray53"], "scrollbar_button_hover": ["gray40", "gray53"],
"segmented_button": ["#979DA2", "#4A4D50"],
"segmented_button_unselected": ["#979DA2", "#4A4D50"],
"segmented_button_unselected_hover": ["gray70", "gray41"],
"tabview_button_frame": ["gray70", "gray35"], "tabview_button_frame": ["gray70", "gray35"],
"tabview_button": ["gray60", "gray45"], "tabview_button": ["gray60", "gray45"],
"tabview_button_hover": ["gray50", "gray55"] "tabview_button_hover": ["gray50", "gray55"]

View File

@ -30,6 +30,12 @@ class DrawEngine:
def __init__(self, canvas: CTkCanvas): def __init__(self, canvas: CTkCanvas):
self._canvas = canvas self._canvas = canvas
self._round_width_to_even_numbers: bool = True
self._round_height_to_even_numbers: bool = True
def set_round_to_even_numbers(self, round_width_to_even_numbers: bool = True, round_height_to_even_numbers: bool = True):
self._round_width_to_even_numbers: bool = round_width_to_even_numbers
self._round_height_to_even_numbers: bool = round_height_to_even_numbers
def __calc_optimal_corner_radius(self, user_corner_radius: Union[float, int]) -> Union[float, int]: def __calc_optimal_corner_radius(self, user_corner_radius: Union[float, int]) -> Union[float, int]:
# optimize for drawing with polygon shapes # optimize for drawing with polygon shapes
@ -55,6 +61,38 @@ class DrawEngine:
else: else:
return user_corner_radius return user_corner_radius
def draw_background_corners(self, width: Union[float, int], height: Union[float, int]):
if self._round_width_to_even_numbers:
width = math.floor(width / 2) * 2 # round (floor) _current_width and _current_height and restrict them to even values only
if self._round_height_to_even_numbers:
height = math.floor(height / 2) * 2
requires_recoloring = False
if not self._canvas.find_withtag("background_corner_top_left"):
self._canvas.create_rectangle((0, 0, 0, 0), tags=("background_parts", "background_corner_top_left"), width=0)
requires_recoloring = True
if not self._canvas.find_withtag("background_corner_top_right"):
self._canvas.create_rectangle((0, 0, 0, 0), tags=("background_parts", "background_corner_top_right"), width=0)
requires_recoloring = True
if not self._canvas.find_withtag("background_corner_bottom_right"):
self._canvas.create_rectangle((0, 0, 0, 0), tags=("background_parts", "background_corner_bottom_right"), width=0)
requires_recoloring = True
if not self._canvas.find_withtag("background_corner_bottom_left"):
self._canvas.create_rectangle((0, 0, 0, 0), tags=("background_parts", "background_corner_bottom_left"), width=0)
requires_recoloring = True
mid_width, mid_height = round(width / 2), round(height / 2)
self._canvas.coords("background_corner_top_left", (0, 0, mid_width, mid_height))
self._canvas.coords("background_corner_top_right", (mid_width, 0, width, mid_height))
self._canvas.coords("background_corner_bottom_right", (mid_width, mid_height, width, height))
self._canvas.coords("background_corner_bottom_left", (0, mid_height, mid_width, height))
if requires_recoloring: # new parts were added -> manage z-order
self._canvas.tag_lower("background_parts")
return requires_recoloring
def draw_rounded_rect_with_border(self, width: Union[float, int], height: Union[float, int], corner_radius: Union[float, int], def draw_rounded_rect_with_border(self, width: Union[float, int], height: Union[float, int], corner_radius: Union[float, int],
border_width: Union[float, int], overwrite_preferred_drawing_method: str = None) -> bool: border_width: Union[float, int], overwrite_preferred_drawing_method: str = None) -> bool:
""" Draws a rounded rectangle with a corner_radius and border_width on the canvas. The border elements have a 'border_parts' tag, """ Draws a rounded rectangle with a corner_radius and border_width on the canvas. The border elements have a 'border_parts' tag,
@ -62,11 +100,13 @@ class DrawEngine:
returns bool if recoloring is necessary """ returns bool if recoloring is necessary """
width = math.floor(width / 2) * 2 # round (floor) _current_width and _current_height and restrict them to even values only if self._round_width_to_even_numbers:
height = math.floor(height / 2) * 2 width = math.floor(width / 2) * 2 # round (floor) _current_width and _current_height and restrict them to even values only
if self._round_height_to_even_numbers:
height = math.floor(height / 2) * 2
corner_radius = round(corner_radius) corner_radius = round(corner_radius)
if corner_radius > width / 2 or corner_radius > height / 2: # restrict corner_radius if it's too larger if corner_radius > width / 2 or corner_radius > height / 2: # restrict corner_radius if it's too large
corner_radius = min(width / 2, height / 2) corner_radius = min(width / 2, height / 2)
border_width = round(border_width) border_width = round(border_width)
@ -139,6 +179,7 @@ class DrawEngine:
if requires_recoloring: # new parts were added -> manage z-order if requires_recoloring: # new parts were added -> manage z-order
self._canvas.tag_lower("inner_parts") self._canvas.tag_lower("inner_parts")
self._canvas.tag_lower("border_parts") self._canvas.tag_lower("border_parts")
self._canvas.tag_lower("background_parts")
return requires_recoloring return requires_recoloring
@ -277,6 +318,7 @@ class DrawEngine:
if requires_recoloring: # new parts were added -> manage z-order if requires_recoloring: # new parts were added -> manage z-order
self._canvas.tag_lower("inner_parts") self._canvas.tag_lower("inner_parts")
self._canvas.tag_lower("border_parts") self._canvas.tag_lower("border_parts")
self._canvas.tag_lower("background_parts")
return requires_recoloring return requires_recoloring
@ -364,8 +406,10 @@ class DrawEngine:
returns bool if recoloring is necessary """ returns bool if recoloring is necessary """
left_section_width = round(left_section_width) left_section_width = round(left_section_width)
width = math.floor(width / 2) * 2 # round (floor) _current_width and _current_height and restrict them to even values only if self._round_width_to_even_numbers:
height = math.floor(height / 2) * 2 width = math.floor(width / 2) * 2 # round (floor) _current_width and _current_height and restrict them to even values only
if self._round_height_to_even_numbers:
height = math.floor(height / 2) * 2
corner_radius = round(corner_radius) corner_radius = round(corner_radius)
if corner_radius > width / 2 or corner_radius > height / 2: # restrict corner_radius if it's too larger if corner_radius > width / 2 or corner_radius > height / 2: # restrict corner_radius if it's too larger
@ -478,6 +522,7 @@ class DrawEngine:
if requires_recoloring: # new parts were added -> manage z-order if requires_recoloring: # new parts were added -> manage z-order
self._canvas.tag_lower("inner_parts") self._canvas.tag_lower("inner_parts")
self._canvas.tag_lower("border_parts") self._canvas.tag_lower("border_parts")
self._canvas.tag_lower("background_parts")
return requires_recoloring return requires_recoloring
@ -641,6 +686,7 @@ class DrawEngine:
if requires_recoloring: # new parts were added -> manage z-order if requires_recoloring: # new parts were added -> manage z-order
self._canvas.tag_lower("inner_parts") self._canvas.tag_lower("inner_parts")
self._canvas.tag_lower("border_parts") self._canvas.tag_lower("border_parts")
self._canvas.tag_lower("background_parts")
return requires_recoloring return requires_recoloring
@ -652,8 +698,10 @@ class DrawEngine:
returns bool if recoloring is necessary """ returns bool if recoloring is necessary """
width = math.floor(width / 2) * 2 # round _current_width and _current_height and restrict them to even values only if self._round_width_to_even_numbers:
height = math.floor(height / 2) * 2 width = math.floor(width / 2) * 2 # round _current_width and _current_height and restrict them to even values only
if self._round_height_to_even_numbers:
height = math.floor(height / 2) * 2
if corner_radius > width / 2 or corner_radius > height / 2: # restrict corner_radius if it's too larger 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) corner_radius = min(width / 2, height / 2)
@ -824,8 +872,10 @@ class DrawEngine:
border_width: Union[float, int], button_length: Union[float, int], button_corner_radius: Union[float, int], border_width: Union[float, int], button_length: Union[float, int], button_corner_radius: Union[float, int],
slider_value: float, orientation: str) -> bool: slider_value: float, orientation: str) -> bool:
width = math.floor(width / 2) * 2 # round _current_width and _current_height and restrict them to even values only if self._round_width_to_even_numbers:
height = math.floor(height / 2) * 2 width = math.floor(width / 2) * 2 # round _current_width and _current_height and restrict them to even values only
if self._round_height_to_even_numbers:
height = math.floor(height / 2) * 2
if corner_radius > width / 2 or corner_radius > height / 2: # restrict corner_radius if it's too larger 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) corner_radius = min(width / 2, height / 2)
@ -980,8 +1030,11 @@ class DrawEngine:
def draw_rounded_scrollbar(self, width: Union[float, int], height: Union[float, int], corner_radius: Union[float, int], 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: 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 self._round_width_to_even_numbers:
width = math.floor(width / 2) * 2 # round _current_width and _current_height and restrict them to even values only
if self._round_height_to_even_numbers:
height = math.floor(height / 2) * 2
if corner_radius > width / 2 or corner_radius > height / 2: # restrict corner_radius if it's too larger 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) corner_radius = min(width / 2, height / 2)

View File

@ -29,6 +29,10 @@ class CTkButton(CTkBaseClass):
text_color: Union[str, Tuple[str, str]] = "default_theme", text_color: Union[str, Tuple[str, str]] = "default_theme",
text_color_disabled: Union[str, Tuple[str, str]] = "default_theme", text_color_disabled: Union[str, Tuple[str, str]] = "default_theme",
background_corner_colors: Tuple[Union[str, Tuple[str, str]]] = None,
round_width_to_even_numbers: bool = True,
round_height_to_even_numbers: bool = True,
text: str = "CTkButton", text: str = "CTkButton",
font: any = "default_theme", font: any = "default_theme",
textvariable: tkinter.Variable = None, textvariable: tkinter.Variable = None,
@ -46,12 +50,17 @@ class CTkButton(CTkBaseClass):
self._fg_color = ThemeManager.theme["color"]["button"] if fg_color == "default_theme" else fg_color self._fg_color = ThemeManager.theme["color"]["button"] if fg_color == "default_theme" else fg_color
self._hover_color = ThemeManager.theme["color"]["button_hover"] if hover_color == "default_theme" else hover_color self._hover_color = ThemeManager.theme["color"]["button_hover"] if hover_color == "default_theme" else hover_color
self._border_color = ThemeManager.theme["color"]["button_border"] if border_color == "default_theme" else border_color self._border_color = ThemeManager.theme["color"]["button_border"] if border_color == "default_theme" else border_color
self._text_color = ThemeManager.theme["color"]["text"] if text_color == "default_theme" else text_color self._text_color = ThemeManager.theme["color"]["text_button"] if text_color == "default_theme" else text_color
self._text_color_disabled = ThemeManager.theme["color"]["text_button_disabled"] if text_color_disabled == "default_theme" else text_color_disabled self._text_color_disabled = ThemeManager.theme["color"]["text_button_disabled"] if text_color_disabled == "default_theme" else text_color_disabled
self._background_corner_colors = background_corner_colors # rendering options for DrawEngine
# shape # shape
self._corner_radius = ThemeManager.theme["shape"]["button_corner_radius"] if corner_radius == "default_theme" else corner_radius self._corner_radius = ThemeManager.theme["shape"]["button_corner_radius"] if corner_radius == "default_theme" else corner_radius
self._border_width = ThemeManager.theme["shape"]["button_border_width"] if border_width == "default_theme" else border_width self._border_width = ThemeManager.theme["shape"]["button_border_width"] if border_width == "default_theme" else border_width
self._round_width_to_even_numbers = round_width_to_even_numbers # rendering options for DrawEngine
self._round_height_to_even_numbers = round_height_to_even_numbers # rendering options for DrawEngine
self._corner_radius = min(self._corner_radius, round(self._current_height/2))
# text, font, image # text, font, image
self._image = image self._image = image
@ -81,6 +90,7 @@ class CTkButton(CTkBaseClass):
height=self._apply_widget_scaling(self._desired_height)) height=self._apply_widget_scaling(self._desired_height))
self._canvas.grid(row=0, column=0, rowspan=2, columnspan=2, sticky="nsew") self._canvas.grid(row=0, column=0, rowspan=2, columnspan=2, sticky="nsew")
self._draw_engine = DrawEngine(self._canvas) self._draw_engine = DrawEngine(self._canvas)
self._draw_engine.set_round_to_even_numbers(self._round_width_to_even_numbers, self._round_height_to_even_numbers) # rendering options
# canvas event bindings # canvas event bindings
self._canvas.bind("<Enter>", self._on_enter) self._canvas.bind("<Enter>", self._on_enter)
@ -115,6 +125,16 @@ class CTkButton(CTkBaseClass):
self._draw() self._draw()
def _draw(self, no_color_updates=False): def _draw(self, no_color_updates=False):
if self._background_corner_colors is not None:
self._draw_engine.draw_background_corners(self._apply_widget_scaling(self._current_width),
self._apply_widget_scaling(self._current_height))
self._canvas.itemconfig("background_corner_top_left", fill=ThemeManager.single_color(self._background_corner_colors[0], self._appearance_mode))
self._canvas.itemconfig("background_corner_top_right", fill=ThemeManager.single_color(self._background_corner_colors[1], self._appearance_mode))
self._canvas.itemconfig("background_corner_bottom_right", fill=ThemeManager.single_color(self._background_corner_colors[2], self._appearance_mode))
self._canvas.itemconfig("background_corner_bottom_left", fill=ThemeManager.single_color(self._background_corner_colors[3], self._appearance_mode))
else:
self._canvas.delete("background_parts")
requires_recoloring = self._draw_engine.draw_rounded_rect_with_border(self._apply_widget_scaling(self._current_width), requires_recoloring = self._draw_engine.draw_rounded_rect_with_border(self._apply_widget_scaling(self._current_width),
self._apply_widget_scaling(self._current_height), self._apply_widget_scaling(self._current_height),
self._apply_widget_scaling(self._corner_radius), self._apply_widget_scaling(self._corner_radius),
@ -267,6 +287,10 @@ class CTkButton(CTkBaseClass):
self._corner_radius = kwargs.pop("corner_radius") self._corner_radius = kwargs.pop("corner_radius")
require_redraw = True require_redraw = True
if "border_width" in kwargs:
self._border_width = kwargs.pop("border_width")
require_redraw = True
if "compound" in kwargs: if "compound" in kwargs:
self._compound = kwargs.pop("compound") self._compound = kwargs.pop("compound")
require_redraw = True require_redraw = True
@ -301,6 +325,10 @@ class CTkButton(CTkBaseClass):
if "height" in kwargs: if "height" in kwargs:
self._set_dimensions(height=kwargs.pop("height")) self._set_dimensions(height=kwargs.pop("height"))
if "background_corner_colors" in kwargs:
self._background_corner_colors = kwargs.pop("background_corner_colors")
require_redraw = True
super().configure(require_redraw=require_redraw, **kwargs) super().configure(require_redraw=require_redraw, **kwargs)
def cget(self, attribute_name: str) -> any: def cget(self, attribute_name: str) -> any:
@ -319,6 +347,8 @@ class CTkButton(CTkBaseClass):
return self._text_color return self._text_color
elif attribute_name == "text_color_disabled": elif attribute_name == "text_color_disabled":
return self._text_color_disabled return self._text_color_disabled
elif attribute_name == "background_corner_colors":
return self._background_corner_colors
elif attribute_name == "text": elif attribute_name == "text":
return self._text return self._text

View File

@ -60,7 +60,7 @@ class CTkComboBox(CTkBaseClass):
# text and font # text and font
self._text_color = ThemeManager.theme["color"]["text"] if text_color == "default_theme" else text_color self._text_color = ThemeManager.theme["color"]["text"] if text_color == "default_theme" else text_color
self._text_color_disabled = ThemeManager.theme["color"]["text_button_disabled"] if text_color_disabled == "default_theme" else text_color_disabled self._text_color_disabled = ThemeManager.theme["color"]["text_disabled"] if text_color_disabled == "default_theme" else text_color_disabled
self._font = (ThemeManager.theme["text"]["font"], ThemeManager.theme["text"]["size"]) if font == "default_theme" else font self._font = (ThemeManager.theme["text"]["font"], ThemeManager.theme["text"]["size"]) if font == "default_theme" else font
# callback and hover functionality # callback and hover functionality

View File

@ -128,6 +128,12 @@ class CTkFrame(CTkBaseClass):
if isinstance(child, CTkBaseClass): if isinstance(child, CTkBaseClass):
child.configure(bg_color=self._fg_color) child.configure(bg_color=self._fg_color)
# only workaround, to enable one layer of passing new bg_color for children with fg_color=None,
# but needs to be abstracted to n-layers somehow
if isinstance(child, CTkFrame) and child.cget("fg_color") is None:
for childrens_child in child.winfo_children():
childrens_child.configure(bg_color=self._fg_color)
if "border_color" in kwargs: if "border_color" in kwargs:
self._border_color = kwargs.pop("border_color") self._border_color = kwargs.pop("border_color")
require_redraw = True require_redraw = True

View File

@ -70,7 +70,7 @@ class CTkLabel(CTkBaseClass):
text_label_grid_sticky = self._anchor if self._anchor != "center" else "" text_label_grid_sticky = self._anchor if self._anchor != "center" else ""
self._text_label.grid(row=0, column=0, sticky=text_label_grid_sticky, self._text_label.grid(row=0, column=0, sticky=text_label_grid_sticky,
padx=min(self._apply_widget_scaling(self._corner_radius), round(self._current_height/2))) padx=self._apply_widget_scaling(min(self._corner_radius, round(self._current_height/2))))
self._check_kwargs_empty(kwargs, raise_error=True) self._check_kwargs_empty(kwargs, raise_error=True)
@ -85,7 +85,7 @@ class CTkLabel(CTkBaseClass):
text_label_grid_sticky = self._anchor if self._anchor != "center" else "" text_label_grid_sticky = self._anchor if self._anchor != "center" else ""
self._text_label.grid(row=0, column=0, sticky=text_label_grid_sticky, self._text_label.grid(row=0, column=0, sticky=text_label_grid_sticky,
padx=min(self._apply_widget_scaling(self._corner_radius), round(self._current_height/2))) padx=self._apply_widget_scaling(min(self._corner_radius, round(self._current_height/2))))
self._draw() self._draw()
@ -125,7 +125,7 @@ class CTkLabel(CTkBaseClass):
self._anchor = kwargs.pop("anchor") self._anchor = kwargs.pop("anchor")
text_label_grid_sticky = self._anchor if self._anchor != "center" else "" text_label_grid_sticky = self._anchor if self._anchor != "center" else ""
self._text_label.grid(row=0, column=0, sticky=text_label_grid_sticky, self._text_label.grid(row=0, column=0, sticky=text_label_grid_sticky,
padx=min(self._apply_widget_scaling(self._corner_radius), round(self._current_height/2))) padx=self._apply_widget_scaling(min(self._corner_radius, round(self._current_height/2))))
if "text" in kwargs: if "text" in kwargs:
self._text = kwargs.pop("text") self._text = kwargs.pop("text")
@ -153,7 +153,7 @@ class CTkLabel(CTkBaseClass):
self._corner_radius = kwargs.pop("corner_radius") self._corner_radius = kwargs.pop("corner_radius")
text_label_grid_sticky = self._anchor if self._anchor != "center" else "" text_label_grid_sticky = self._anchor if self._anchor != "center" else ""
self._text_label.grid(row=0, column=0, sticky=text_label_grid_sticky, self._text_label.grid(row=0, column=0, sticky=text_label_grid_sticky,
padx=min(self._apply_widget_scaling(self._corner_radius), round(self._current_height/2))) padx=self._apply_widget_scaling(min(self._corner_radius, round(self._current_height/2))))
require_redraw = True require_redraw = True
self._text_label.configure(**pop_from_dict_by_set(kwargs, self._valid_tk_label_attributes)) # configure tkinter.Label self._text_label.configure(**pop_from_dict_by_set(kwargs, self._valid_tk_label_attributes)) # configure tkinter.Label

View File

@ -54,7 +54,7 @@ class CTkOptionMenu(CTkBaseClass):
self._corner_radius = ThemeManager.theme["shape"]["button_corner_radius"] if corner_radius == "default_theme" else corner_radius self._corner_radius = ThemeManager.theme["shape"]["button_corner_radius"] if corner_radius == "default_theme" else corner_radius
# text and font # text and font
self._text_color = ThemeManager.theme["color"]["text"] if text_color == "default_theme" else text_color self._text_color = ThemeManager.theme["color"]["text_button"] if text_color == "default_theme" else text_color
self._text_color_disabled = ThemeManager.theme["color"]["text_button_disabled"] if text_color_disabled == "default_theme" else text_color_disabled self._text_color_disabled = ThemeManager.theme["color"]["text_button_disabled"] if text_color_disabled == "default_theme" else text_color_disabled
self._font = (ThemeManager.theme["text"]["font"], ThemeManager.theme["text"]["size"]) if font == "default_theme" else font self._font = (ThemeManager.theme["text"]["font"], ThemeManager.theme["text"]["size"]) if font == "default_theme" else font
self._dropdown_text_font = dropdown_text_font self._dropdown_text_font = dropdown_text_font
@ -156,6 +156,12 @@ class CTkOptionMenu(CTkBaseClass):
height=self._apply_widget_scaling(self._desired_height)) height=self._apply_widget_scaling(self._desired_height))
self._draw() self._draw()
def destroy(self):
if self._variable is not None: # remove old callback
self._variable.trace_remove("write", self._variable_callback_name)
super().destroy()
def _draw(self, no_color_updates=False): def _draw(self, no_color_updates=False):
left_section_width = self._current_width - self._current_height left_section_width = self._current_width - self._current_height
requires_recoloring = self._draw_engine.draw_rounded_rect_with_border_vertical_split(self._apply_widget_scaling(self._current_width), requires_recoloring = self._draw_engine.draw_rounded_rect_with_border_vertical_split(self._apply_widget_scaling(self._current_width),

View File

@ -1,19 +1,332 @@
import tkinter import tkinter
from typing import Union, Tuple from typing import Union, Tuple, List, Dict
from .widget_base_class import CTkBaseClass from ..theme_manager import ThemeManager
from .ctk_button import CTkButton from .ctk_button import CTkButton
from .ctk_frame import CTkFrame
class CTkSegmentedButton(CTkBaseClass): class CTkSegmentedButton(CTkFrame):
"""
Segmented button with corner radius, border width, variable support.
For detailed information check out the documentation.
"""
def __init__(self, def __init__(self,
master: any = None, master: any = None,
width: int = 140,
height: int = 28,
corner_radius: Union[int, str] = "default_theme",
border_width: Union[int, str] = 3,
bg_color: Union[str, Tuple[str, str], None] = None, bg_color: Union[str, Tuple[str, str], None] = None,
fg_color: Union[str, Tuple[str, str], None] = "default_theme", fg_color: Union[str, Tuple[str, str], None] = "default_theme",
hover_color: Union[str, Tuple[str, str]] = "default_theme", selected_color: Union[str, Tuple[str, str]] = "default_theme",
selected_hover_color: Union[str, Tuple[str, str]] = "default_theme",
unselected_color: Union[str, Tuple[str, str]] = "default_theme",
unselected_hover_color: Union[str, Tuple[str, str]] = "default_theme",
text_color: Union[str, Tuple[str, str]] = "default_theme", text_color: Union[str, Tuple[str, str]] = "default_theme",
text_color_disabled: Union[str, Tuple[str, str]] = "default_theme", text_color_disabled: Union[str, Tuple[str, str]] = "default_theme",
values: list = None): values: list = None,
super().__init__(master=master, ) variable: tkinter.Variable = None,
dynamic_resizing: bool = True,
**kwargs):
super().__init__(master=master, bg_color=bg_color, width=width, height=height, **kwargs)
self._sb_fg_color = ThemeManager.theme["color"]["segmented_button"] if fg_color == "default_theme" else fg_color
self._sb_selected_color = ThemeManager.theme["color"]["button"] if selected_color == "default_theme" else selected_color
self._sb_selected_hover_color = ThemeManager.theme["color"]["button_hover"] if selected_hover_color == "default_theme" else selected_hover_color
self._sb_unselected_color = ThemeManager.theme["color"]["segmented_button_unselected"] if unselected_color == "default_theme" else unselected_color
self._sb_unselected_hover_color = ThemeManager.theme["color"]["segmented_button_unselected_hover"] if unselected_hover_color == "default_theme" else unselected_hover_color
self._sb_text_color = ThemeManager.theme["color"]["text_button"] if text_color == "default_theme" else text_color
self._sb_text_color_disabled = ThemeManager.theme["color"]["text_button_disabled"] if text_color_disabled == "default_theme" else text_color_disabled
self._sb_corner_radius = ThemeManager.theme["shape"]["button_corner_radius"] if corner_radius == "default_theme" else corner_radius
self._sb_border_width = ThemeManager.theme["shape"]["button_border_width"] if border_width == "default_theme" else border_width
self._buttons_dict: Dict[str, CTkButton] = {} # mapped from value to button object
if values is None:
self._value_list: List[str] = ["CTkSegmentedButton"]
elif len(values) == 0:
raise ValueError("values of CTkSegmentedButton can not be empty")
else:
self._value_list: List[str] = values # Values ordered like buttons rendered on widget
self._dynamic_resizing = dynamic_resizing
if not self._dynamic_resizing:
self.grid_propagate(0)
self._check_unique_values(self._value_list)
self._current_value: str = ""
self._create_buttons_from_values()
self._create_button_grid()
self._variable = variable
self._variable_callback_blocked: bool = False
self._variable_callback_name: Union[str, None] = None
if self._variable is not None:
self._variable_callback_name = self._variable.trace_add("write", self._variable_callback)
self.set(self._variable.get(), from_variable_callback=True)
super().configure(corner_radius=self._sb_corner_radius)
def destroy(self):
if self._variable is not None: # remove old callback
self._variable.trace_remove("write", self._variable_callback_name)
super().destroy()
def _variable_callback(self, var_name, index, mode):
if not self._variable_callback_blocked:
self.set(self._variable.get(), from_variable_callback=True)
def _get_index_by_value(self, value: str):
for index, value_from_list in enumerate(self._value_list):
if value_from_list == value:
return index
raise ValueError(f"CTkSegmentedButton does not contain value '{value}'")
def _configure_button_corners_for_index(self, index: int):
if index == 0 and len(self._value_list) == 1:
self._buttons_dict[self._value_list[index]].configure(background_corner_colors=(self._bg_color, self._bg_color, self._bg_color, self._bg_color))
elif index == 0:
self._buttons_dict[self._value_list[index]].configure(background_corner_colors=(self._bg_color, self._sb_fg_color, self._sb_fg_color, self._bg_color))
elif index == len(self._value_list) - 1:
self._buttons_dict[self._value_list[index]].configure(background_corner_colors=(self._sb_fg_color, self._bg_color, self._bg_color, self._sb_fg_color))
else:
self._buttons_dict[self._value_list[index]].configure(background_corner_colors=(self._sb_fg_color, self._sb_fg_color, self._sb_fg_color, self._sb_fg_color))
def _unselect_button_by_value(self, value: str):
if value in self._buttons_dict:
self._buttons_dict[value].configure(fg_color=self._sb_unselected_color,
hover_color=self._sb_unselected_hover_color)
def _select_button_by_value(self, value: str):
if self._current_value is not None and self._current_value != "":
self._unselect_button_by_value(self._current_value)
self._current_value = value
self._buttons_dict[value].configure(fg_color=self._sb_selected_color,
hover_color=self._sb_selected_hover_color)
def _create_button(self, index: int, value: str) -> CTkButton:
new_button = CTkButton(self,
height=self._current_height,
width=0,
corner_radius=self._sb_corner_radius,
text=value,
border_width=self._sb_border_width,
border_color=self._sb_fg_color,
fg_color=self._sb_unselected_color,
hover_color=self._sb_unselected_hover_color,
text_color=self._sb_text_color,
text_color_disabled=self._sb_text_color_disabled,
command=lambda v=value: self.set(v),
background_corner_colors=None,
round_width_to_even_numbers=False) # DrawEngine rendering option (so that theres no gap between buttons)
return new_button
@staticmethod
def _check_unique_values(values: List[str]):
""" raises exception if values are not unique """
if len(values) != len(set(values)):
raise ValueError("CTkSegmentedButton values are not unique")
def _create_button_grid(self):
# remove minsize from every grid cell in the first row
number_of_columns, _ = self.grid_size()
for n in range(number_of_columns):
self.grid_columnconfigure(n, weight=1, minsize=0)
self.grid_rowconfigure(0, weight=1)
for index, value in enumerate(self._value_list):
self.grid_columnconfigure(index, weight=1, minsize=self._current_height)
self._buttons_dict[value].grid(row=0, column=index, sticky="ew")
def _create_buttons_from_values(self):
assert len(self._buttons_dict) == 0
assert len(self._value_list) > 0
for index, value in enumerate(self._value_list):
self._buttons_dict[value] = self._create_button(index, value)
self._configure_button_corners_for_index(index)
def configure(self, **kwargs):
if "height" in kwargs:
for button in self._buttons_dict.values():
button.configure(height=kwargs["height"])
if "bg_color" in kwargs:
super().configure(bg_color=kwargs.pop("bg_color"))
if len(self._buttons_dict) > 0:
self._configure_button_corners_for_index(0)
if len(self._buttons_dict) > 1:
max_index = len(self._buttons_dict) - 1
self._configure_button_corners_for_index(max_index)
if "fg_color" in kwargs:
self._sb_fg_color = kwargs.pop("fg_color")
for index, button in enumerate(self._buttons_dict.values()):
button.configure(border_color=self._sb_fg_color)
self._configure_button_corners_for_index(index)
if "selected_color" in kwargs:
self._sb_selected_color = kwargs.pop("selected_color")
if self._current_value in self._buttons_dict:
self._buttons_dict[self._current_value].configure(fg_color=self._sb_selected_color)
if "selected_hover_color" in kwargs:
self._sb_selected_hover_color = kwargs.pop("selected_hover_color")
if self._current_value in self._buttons_dict:
self._buttons_dict[self._current_value].configure(hover_color=self._sb_selected_hover_color)
if "unselected_color" in kwargs:
self._sb_unselected_color = kwargs.pop("unselected_color")
for value, button in self._buttons_dict.items():
if value != self._current_value:
button.configure(fg_color=self._sb_unselected_color)
if "unselected_hover_color" in kwargs:
self._sb_unselected_hover_color = kwargs.pop("unselected_hover_color")
for value, button in self._buttons_dict.items():
if value != self._current_value:
button.configure(hover_color=self._sb_unselected_hover_color)
if "text_color" in kwargs:
self._sb_text_color = kwargs.pop("text_color")
for button in self._buttons_dict.values():
button.configure(text_color=self._sb_text_color)
if "text_color_disabled" in kwargs:
self._sb_text_color_disabled = kwargs.pop("text_color_disabled")
for button in self._buttons_dict.values():
button.configure(text_color_disabled=self._sb_text_color_disabled)
if "values" in kwargs:
for button in self._buttons_dict.values():
button.destroy()
self._buttons_dict.clear()
self._value_list = kwargs.pop("values")
self._current_value = ""
if len(self._value_list) == 0:
raise ValueError("len() of values of CTkSegmentedButton can not be zero")
self._check_unique_values(self._value_list)
self._create_buttons_from_values()
self._create_button_grid()
if "variable" in kwargs:
if self._variable is not None: # remove old callback
self._variable.trace_remove("write", self._variable_callback_name)
self._variable = kwargs.pop("variable")
if self._variable is not None and self._variable != "":
self._variable_callback_name = self._variable.trace_add("write", self._variable_callback)
self.set(self._variable.get(), from_variable_callback=True)
else:
self._variable = None
if "dynamic_resizing" in kwargs:
self._dynamic_resizing = kwargs.pop("dynamic_resizing")
if not self._dynamic_resizing:
self.grid_propagate(0)
else:
self.grid_propagate(1)
super().configure(**kwargs)
def cget(self, attribute_name: str) -> any:
if attribute_name == "corner_radius":
return self._sb_corner_radius
elif attribute_name == "border_width":
return self._sb_border_width
elif attribute_name == "fg_color":
return self._sb_fg_color
elif attribute_name == "selected_color":
return self._sb_selected_color
elif attribute_name == "selected_hover_color":
return self._sb_selected_hover_color
elif attribute_name == "unselected_color":
return self._sb_unselected_color
elif attribute_name == "unselected_hover_color":
return self._sb_unselected_hover_color
elif attribute_name == "text_color":
return self._sb_text_color
elif attribute_name == "text_color_disabled":
return self._sb_text_color_disabled
elif attribute_name == "values":
return self._value_list
elif attribute_name == "variable":
return self._variable
elif attribute_name == "dynamic_resizing":
return self._dynamic_resizing
else:
return super().cget(attribute_name)
def set(self, value: str, from_variable_callback: bool = False):
if value == self._current_value:
print("value == self._current_value")
elif value in self._buttons_dict:
self._select_button_by_value(value)
if self._variable is not None and not from_variable_callback:
self._variable_callback_blocked = True
self._variable.set(value)
self._variable_callback_blocked = False
else:
if self._current_value in self._buttons_dict:
self._unselect_button_by_value(self._current_value)
self._current_value = value
if self._variable is not None and not from_variable_callback:
self._variable_callback_blocked = True
self._variable.set(value)
self._variable_callback_blocked = False
def get(self) -> str:
return self._current_value
def insert_value(self, index: int, value: str):
if value not in self._buttons_dict:
self._value_list.insert(index, value)
self._buttons_dict[value] = self._create_button(index, value)
self._configure_button_corners_for_index(index)
if index > 0:
self._configure_button_corners_for_index(index - 1)
if index < len(self._buttons_dict) - 1:
self._configure_button_corners_for_index(index + 1)
self._create_button_grid()
if value == self._current_value:
self._select_button_by_value(self._current_value)
else:
raise ValueError(f"CTkSegmentedButton can not insert value '{value}', already part of the values")
def remove_value(self, value: str):
if value in self._buttons_dict:
self._buttons_dict[value].destroy()
self._buttons_dict.pop(value)
index_to_remove = self._get_index_by_value(value)
self._value_list.pop(index_to_remove)
if index_to_remove <= len(self._buttons_dict) - 1:
self._configure_button_corners_for_index(index_to_remove)
self._create_button_grid()
else:
raise ValueError(f"CTkSegmentedButton does not contain value '{value}'")

View File

@ -102,30 +102,78 @@ class CTkBaseClass(tkinter.Frame):
return False return False
def destroy(self): def destroy(self):
""" Destroy this and all descendants widgets. """
AppearanceModeTracker.remove(self._set_appearance_mode) AppearanceModeTracker.remove(self._set_appearance_mode)
super().destroy() super().destroy()
def place(self, **kwargs): def place(self, **kwargs):
"""
Place a widget in the parent widget. Use as options:
in=master - master relative to which the widget is placed
in_=master - see 'in' option description
x=amount - locate anchor of this widget at position x of master
y=amount - locate anchor of this widget at position y of master
relx=amount - locate anchor of this widget between 0.0 and 1.0 relative to width of master (1.0 is right edge)
rely=amount - locate anchor of this widget between 0.0 and 1.0 relative to height of master (1.0 is bottom edge)
anchor=NSEW (or subset) - position anchor according to given direction
width=amount - width of this widget in pixel
height=amount - height of this widget in pixel
relwidth=amount - width of this widget between 0.0 and 1.0 relative to width of master (1.0 is the same width as the master)
relheight=amount - height of this widget between 0.0 and 1.0 relative to height of master (1.0 is the same height as the master)
bordermode="inside" or "outside" - whether to take border width of master widget into account
"""
self._last_geometry_manager_call = {"function": super().place, "kwargs": kwargs} self._last_geometry_manager_call = {"function": super().place, "kwargs": kwargs}
return super().place(**self._apply_argument_scaling(kwargs)) return super().place(**self._apply_argument_scaling(kwargs))
def place_forget(self): def place_forget(self):
""" Unmap this widget. """
self._last_geometry_manager_call = None self._last_geometry_manager_call = None
return super().place_forget() return super().place_forget()
def pack(self, **kwargs): def pack(self, **kwargs):
"""
Pack a widget in the parent widget. Use as options:
after=widget - pack it after you have packed widget
anchor=NSEW (or subset) - position widget according to given direction
before=widget - pack it before you will pack widget
expand=bool - expand widget if parent size grows
fill=NONE or X or Y or BOTH - fill widget if widget grows
in=master - use master to contain this widget
in_=master - see 'in' option description
ipadx=amount - add internal padding in x direction
ipady=amount - add internal padding in y direction
padx=amount - add padding in x direction
pady=amount - add padding in y direction
side=TOP or BOTTOM or LEFT or RIGHT - where to add this widget.
"""
self._last_geometry_manager_call = {"function": super().pack, "kwargs": kwargs} self._last_geometry_manager_call = {"function": super().pack, "kwargs": kwargs}
return super().pack(**self._apply_argument_scaling(kwargs)) return super().pack(**self._apply_argument_scaling(kwargs))
def pack_forget(self): def pack_forget(self):
""" Unmap this widget and do not use it for the packing order. """
self._last_geometry_manager_call = None self._last_geometry_manager_call = None
return super().pack_forget() return super().pack_forget()
def grid(self, **kwargs): def grid(self, **kwargs):
"""
Position a widget in the parent widget in a grid. Use as options:
column=number - use cell identified with given column (starting with 0)
columnspan=number - this widget will span several columns
in=master - use master to contain this widget
in_=master - see 'in' option description
ipadx=amount - add internal padding in x direction
ipady=amount - add internal padding in y direction
padx=amount - add padding in x direction
pady=amount - add padding in y direction
row=number - use cell identified with given row (starting with 0)
rowspan=number - this widget will span several rows
sticky=NSEW - if cell is larger on which sides will this widget stick to the cell boundary
"""
self._last_geometry_manager_call = {"function": super().grid, "kwargs": kwargs} self._last_geometry_manager_call = {"function": super().grid, "kwargs": kwargs}
return super().grid(**self._apply_argument_scaling(kwargs)) return super().grid(**self._apply_argument_scaling(kwargs))
def grid_forget(self): def grid_forget(self):
""" Unmap this widget. """
self._last_geometry_manager_call = None self._last_geometry_manager_call = None
return super().grid_forget() return super().grid_forget()
@ -158,7 +206,7 @@ class CTkBaseClass(tkinter.Frame):
raise AttributeError("'config' is not implemented for CTk widgets. For consistency, always use 'configure' instead.") raise AttributeError("'config' is not implemented for CTk widgets. For consistency, always use 'configure' instead.")
def configure(self, require_redraw=False, **kwargs): def configure(self, require_redraw=False, **kwargs):
""" basic configure with bg_color support, calls configure of tkinter.Frame, calls draw() in the end """ """ basic configure with bg_color support, calls configure of tkinter.Frame, updates in the end """
if "bg_color" in kwargs: if "bg_color" in kwargs:
new_bg_color = kwargs.pop("bg_color") new_bg_color = kwargs.pop("bg_color")

View File

@ -98,17 +98,22 @@ class App(customtkinter.CTk):
self.slider_progressbar_frame = customtkinter.CTkFrame(self, fg_color=None) self.slider_progressbar_frame = customtkinter.CTkFrame(self, fg_color=None)
self.slider_progressbar_frame.grid(row=1, column=1, columnspan=2, padx=(20, 10), pady=(10, 10), sticky="nsew") self.slider_progressbar_frame.grid(row=1, column=1, columnspan=2, padx=(20, 10), pady=(10, 10), sticky="nsew")
self.slider_progressbar_frame.grid_columnconfigure(0, weight=1) self.slider_progressbar_frame.grid_columnconfigure(0, weight=1)
self.slider_progressbar_frame.grid_rowconfigure(3, weight=1) self.slider_progressbar_frame.grid_rowconfigure(4, weight=1)
self.seg_button = customtkinter._CTkSegmentedButton(self.slider_progressbar_frame, corner_radius=1000,
values=["CTkSegmentedButton", "Value 2", "Value 3"])
self.seg_button.grid(row=0, column=0, padx=(20, 10), pady=(10, 10), sticky="ew")
self.progressbar_1 = customtkinter.CTkProgressBar(self.slider_progressbar_frame) self.progressbar_1 = customtkinter.CTkProgressBar(self.slider_progressbar_frame)
self.progressbar_1.grid(row=0, column=0, padx=(20, 10), pady=(10, 10), sticky="ew") self.progressbar_1.grid(row=1, column=0, padx=(20, 10), pady=(10, 10), sticky="ew")
self.progressbar_2 = customtkinter.CTkProgressBar(self.slider_progressbar_frame) self.progressbar_2 = customtkinter.CTkProgressBar(self.slider_progressbar_frame)
self.progressbar_2.grid(row=1, column=0, padx=(20, 10), pady=(10, 10), sticky="ew") self.progressbar_2.grid(row=2, column=0, padx=(20, 10), pady=(10, 10), sticky="ew")
self.slider_1 = customtkinter.CTkSlider(self.slider_progressbar_frame, from_=0, to=1, number_of_steps=4) self.slider_1 = customtkinter.CTkSlider(self.slider_progressbar_frame, from_=0, to=1, number_of_steps=4)
self.slider_1.grid(row=2, column=0, padx=(20, 10), pady=(10, 10), sticky="ew") self.slider_1.grid(row=3, column=0, padx=(20, 10), pady=(10, 10), sticky="ew")
self.slider_2 = customtkinter.CTkSlider(self.slider_progressbar_frame, orientation="vertical") self.slider_2 = customtkinter.CTkSlider(self.slider_progressbar_frame, orientation="vertical")
self.slider_2.grid(row=0, column=1, rowspan=4, padx=(10, 10), pady=(10, 10), sticky="ns") self.slider_2.grid(row=0, column=1, rowspan=5, padx=(10, 10), pady=(10, 10), sticky="ns")
self.progressbar_3 = customtkinter.CTkProgressBar(self.slider_progressbar_frame, orientation="vertical") self.progressbar_3 = customtkinter.CTkProgressBar(self.slider_progressbar_frame, orientation="vertical")
self.progressbar_3.grid(row=0, column=2, rowspan=4, padx=(10, 20), pady=(10, 10), sticky="ns") self.progressbar_3.grid(row=0, column=2, rowspan=5, padx=(10, 20), pady=(10, 10), sticky="ns")
# set default values # set default values
self.sidebar_button_3.configure(state="disabled", text="Disabled CTkButton") self.sidebar_button_3.configure(state="disabled", text="Disabled CTkButton")

View File

@ -0,0 +1,72 @@
import customtkinter
app = customtkinter.CTk()
app.geometry("600x950")
switch_1 = customtkinter.CTkSwitch(app, text="darkmode", command=lambda: customtkinter.set_appearance_mode("dark" if switch_1.get() == 1 else "light"))
switch_1.pack(padx=20, pady=20)
seg_1 = customtkinter._CTkSegmentedButton(app, values=["value 1", "Value 2", "Value 42", "Value 123", "longlonglong"])
seg_1.pack(padx=20, pady=20)
frame_1 = customtkinter.CTkFrame(app, height=100)
frame_1.pack(padx=20, pady=20, fill="x")
seg_2_var = customtkinter.StringVar(value="value 1")
seg_2 = customtkinter._CTkSegmentedButton(frame_1, values=["value 1", "Value 2", "Value 42"], variable=seg_2_var)
seg_2.pack(padx=20, pady=20)
seg_2.insert_value(0, "insert at 0")
seg_2.insert_value(1, "insert at 1")
label_seg_2 = customtkinter.CTkLabel(frame_1, textvariable=seg_2_var)
label_seg_2.pack(padx=20, pady=20)
frame_1_1 = customtkinter.CTkFrame(frame_1, height=100)
frame_1_1.pack(padx=20, pady=20, fill="x")
switch_2 = customtkinter.CTkSwitch(frame_1_1, text="change fg", command=lambda: frame_1_1.configure(fg_color="red" if switch_2.get() == 1 else "green"))
switch_2.pack(padx=20, pady=20)
seg_3 = customtkinter._CTkSegmentedButton(frame_1_1, values=["value 1", "Value 2", "Value 42"])
seg_3.pack(padx=20, pady=20)
seg_4 = customtkinter._CTkSegmentedButton(app)
seg_4.pack(padx=20, pady=20)
seg_5_var = customtkinter.StringVar(value="kfasjkfdklaj")
seg_5 = customtkinter._CTkSegmentedButton(app, corner_radius=1000, border_width=0, unselected_color="green",
variable=seg_5_var)
seg_5.pack(padx=20, pady=20)
seg_5.configure(values=["1", "2", "3", "4"])
seg_5.insert_value(0, "insert begin")
seg_5.insert_value(len(seg_5.cget("values")), "insert 1")
seg_5.insert_value(len(seg_5.cget("values")), "insert 2")
seg_5.insert_value(len(seg_5.cget("values")), "insert 3")
seg_5.configure(fg_color="green")
seg_5.set("insert 2")
seg_5.remove_value("insert 2")
label_seg_5 = customtkinter.CTkLabel(app, textvariable=seg_5_var)
label_seg_5.pack(padx=20, pady=20)
seg_6_var = customtkinter.StringVar(value="kfasjkfdklaj")
seg_6 = customtkinter._CTkSegmentedButton(app, width=300)
seg_6.pack(padx=20, pady=20)
entry_6 = customtkinter.CTkEntry(app)
entry_6.pack(padx=20, pady=(0, 20))
button_6 = customtkinter.CTkButton(app, text="set", command=lambda: seg_6.set(entry_6.get()))
button_6.pack(padx=20, pady=(0, 20))
button_6 = customtkinter.CTkButton(app, text="insert value", command=lambda: seg_6.insert_value(0, entry_6.get()))
button_6.pack(padx=20, pady=(0, 20))
label_6 = customtkinter.CTkLabel(app, textvariable=seg_6_var)
label_6.pack(padx=20, pady=(0, 20))
seg_6.configure(height=50, variable=seg_6_var)
seg_6.remove_value("CTkSegmentedButton")
seg_6.configure(values=[])
app.mainloop()