mirror of
				https://github.com/TomSchimansky/CustomTkinter.git
				synced 2023-08-10 21:13:13 +03:00 
			
		
		
		
	finished CTkSegmentedButton, created test_segmented_button.py
This commit is contained in:
		| @@ -65,6 +65,7 @@ from .widgets.ctk_combobox import CTkComboBox | ||||
| from .widgets.ctk_scrollbar import CTkScrollbar | ||||
| from .widgets.ctk_textbox import CTkTextbox | ||||
| from .widgets.ctk_tabview import CTkTabview as _CTkTabview | ||||
| from .widgets.ctk_segmented_button import CTkSegmentedButton as _CTkSegmentedButton | ||||
|  | ||||
| # import windows | ||||
| from .windows.ctk_tk import CTk | ||||
|   | ||||
| @@ -5,7 +5,7 @@ | ||||
|     "button_hover": ["#36719F", "#144870"], | ||||
|     "button_border": ["#3E454A", "#949A9F"], | ||||
|     "checkbox_border": ["#3E454A", "#949A9F"], | ||||
|     "checkmark": ["white", "gray90"], | ||||
|     "checkmark": ["#DCE4EE", "gray90"], | ||||
|     "entry": ["#F9F9FA", "#343638"], | ||||
|     "entry_border": ["#979DA2", "#565B5E"], | ||||
|     "entry_placeholder_text": ["gray52", "gray62"], | ||||
| @@ -15,7 +15,8 @@ | ||||
|     "label": [null, null], | ||||
|     "text": ["gray10", "#DCE4EE"], | ||||
|     "text_disabled": ["gray60", "#777B80"], | ||||
|     "text_button_disabled": ["gray40", "gray74"], | ||||
|     "text_button": ["#DCE4EE", "#DCE4EE"], | ||||
|     "text_button_disabled": ["gray74", "gray74"], | ||||
|     "progressbar": ["#939BA2", "#4A4D50"], | ||||
|     "progressbar_progress": ["#3B8ED0", "#1F6AA5"], | ||||
|     "progressbar_border": ["gray", "gray"], | ||||
| @@ -36,6 +37,9 @@ | ||||
|     "dropdown_text": ["gray10", "#DCE4EE"], | ||||
|     "scrollbar_button": ["gray55", "gray41"], | ||||
|     "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": ["gray60", "gray45"], | ||||
|     "tabview_button_hover": ["gray50", "gray55"] | ||||
|   | ||||
| @@ -30,6 +30,12 @@ class DrawEngine: | ||||
|  | ||||
|     def __init__(self, canvas: CTkCanvas): | ||||
|         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]: | ||||
|         # optimize for drawing with polygon shapes | ||||
| @@ -55,6 +61,38 @@ class DrawEngine: | ||||
|             else: | ||||
|                 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], | ||||
|                                       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, | ||||
| @@ -62,11 +100,13 @@ class DrawEngine: | ||||
|  | ||||
|             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 | ||||
|         height = math.floor(height / 2) * 2 | ||||
|         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 | ||||
|         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) | ||||
|  | ||||
|         border_width = round(border_width) | ||||
| @@ -139,6 +179,7 @@ class DrawEngine: | ||||
|         if requires_recoloring:  # new parts were added -> manage z-order | ||||
|             self._canvas.tag_lower("inner_parts") | ||||
|             self._canvas.tag_lower("border_parts") | ||||
|             self._canvas.tag_lower("background_parts") | ||||
|  | ||||
|         return requires_recoloring | ||||
|  | ||||
| @@ -277,6 +318,7 @@ class DrawEngine: | ||||
|         if requires_recoloring:  # new parts were added -> manage z-order | ||||
|             self._canvas.tag_lower("inner_parts") | ||||
|             self._canvas.tag_lower("border_parts") | ||||
|             self._canvas.tag_lower("background_parts") | ||||
|  | ||||
|         return requires_recoloring | ||||
|  | ||||
| @@ -364,8 +406,10 @@ class DrawEngine: | ||||
|             returns bool if recoloring is necessary """ | ||||
|  | ||||
|         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 | ||||
|         height = math.floor(height / 2) * 2 | ||||
|         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 | ||||
|         corner_radius = round(corner_radius) | ||||
|  | ||||
|         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 | ||||
|             self._canvas.tag_lower("inner_parts") | ||||
|             self._canvas.tag_lower("border_parts") | ||||
|             self._canvas.tag_lower("background_parts") | ||||
|  | ||||
|         return requires_recoloring | ||||
|  | ||||
| @@ -641,6 +686,7 @@ class DrawEngine: | ||||
|         if requires_recoloring:  # new parts were added -> manage z-order | ||||
|             self._canvas.tag_lower("inner_parts") | ||||
|             self._canvas.tag_lower("border_parts") | ||||
|             self._canvas.tag_lower("background_parts") | ||||
|  | ||||
|         return requires_recoloring | ||||
|  | ||||
| @@ -652,8 +698,10 @@ class DrawEngine: | ||||
|  | ||||
|             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 | ||||
|         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 | ||||
|             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], | ||||
|                                                    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 | ||||
|         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 | ||||
|             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], | ||||
|                                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 | ||||
|             corner_radius = min(width / 2, height / 2) | ||||
|   | ||||
| @@ -29,6 +29,10 @@ class CTkButton(CTkBaseClass): | ||||
|                  text_color: 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", | ||||
|                  font: any = "default_theme", | ||||
|                  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._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._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._background_corner_colors = background_corner_colors  # rendering options for DrawEngine | ||||
|  | ||||
|         # shape | ||||
|         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._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 | ||||
|         self._image = image | ||||
| @@ -81,6 +90,7 @@ class CTkButton(CTkBaseClass): | ||||
|                                  height=self._apply_widget_scaling(self._desired_height)) | ||||
|         self._canvas.grid(row=0, column=0, rowspan=2, columnspan=2, sticky="nsew") | ||||
|         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 | ||||
|         self._canvas.bind("<Enter>", self._on_enter) | ||||
| @@ -115,6 +125,16 @@ class CTkButton(CTkBaseClass): | ||||
|         self._draw() | ||||
|  | ||||
|     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), | ||||
|                                                                               self._apply_widget_scaling(self._current_height), | ||||
|                                                                               self._apply_widget_scaling(self._corner_radius), | ||||
| @@ -267,6 +287,10 @@ class CTkButton(CTkBaseClass): | ||||
|             self._corner_radius = kwargs.pop("corner_radius") | ||||
|             require_redraw = True | ||||
|  | ||||
|         if "border_width" in kwargs: | ||||
|             self._border_width = kwargs.pop("border_width") | ||||
|             require_redraw = True | ||||
|  | ||||
|         if "compound" in kwargs: | ||||
|             self._compound = kwargs.pop("compound") | ||||
|             require_redraw = True | ||||
| @@ -301,6 +325,10 @@ class CTkButton(CTkBaseClass): | ||||
|         if "height" in kwargs: | ||||
|             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) | ||||
|  | ||||
|     def cget(self, attribute_name: str) -> any: | ||||
| @@ -319,6 +347,8 @@ class CTkButton(CTkBaseClass): | ||||
|             return self._text_color | ||||
|         elif attribute_name == "text_color_disabled": | ||||
|             return self._text_color_disabled | ||||
|         elif attribute_name == "background_corner_colors": | ||||
|             return self._background_corner_colors | ||||
|  | ||||
|         elif attribute_name == "text": | ||||
|             return self._text | ||||
|   | ||||
| @@ -60,7 +60,7 @@ class CTkComboBox(CTkBaseClass): | ||||
|  | ||||
|         # text and font | ||||
|         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 | ||||
|  | ||||
|         # callback and hover functionality | ||||
|   | ||||
| @@ -128,6 +128,12 @@ class CTkFrame(CTkBaseClass): | ||||
|                 if isinstance(child, CTkBaseClass): | ||||
|                     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: | ||||
|             self._border_color = kwargs.pop("border_color") | ||||
|             require_redraw = True | ||||
|   | ||||
| @@ -70,7 +70,7 @@ class CTkLabel(CTkBaseClass): | ||||
|  | ||||
|         text_label_grid_sticky = self._anchor if self._anchor != "center" else "" | ||||
|         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) | ||||
|  | ||||
| @@ -85,7 +85,7 @@ class CTkLabel(CTkBaseClass): | ||||
|  | ||||
|         text_label_grid_sticky = self._anchor if self._anchor != "center" else "" | ||||
|         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() | ||||
|  | ||||
| @@ -125,7 +125,7 @@ class CTkLabel(CTkBaseClass): | ||||
|             self._anchor = kwargs.pop("anchor") | ||||
|             text_label_grid_sticky = self._anchor if self._anchor != "center" else "" | ||||
|             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: | ||||
|             self._text = kwargs.pop("text") | ||||
| @@ -153,7 +153,7 @@ class CTkLabel(CTkBaseClass): | ||||
|             self._corner_radius = kwargs.pop("corner_radius") | ||||
|             text_label_grid_sticky = self._anchor if self._anchor != "center" else "" | ||||
|             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 | ||||
|  | ||||
|         self._text_label.configure(**pop_from_dict_by_set(kwargs, self._valid_tk_label_attributes))  # configure tkinter.Label | ||||
|   | ||||
| @@ -54,7 +54,7 @@ class CTkOptionMenu(CTkBaseClass): | ||||
|         self._corner_radius = ThemeManager.theme["shape"]["button_corner_radius"] if corner_radius == "default_theme" else corner_radius | ||||
|  | ||||
|         # 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._font = (ThemeManager.theme["text"]["font"], ThemeManager.theme["text"]["size"]) if font == "default_theme" else font | ||||
|         self._dropdown_text_font = dropdown_text_font | ||||
| @@ -156,6 +156,12 @@ class CTkOptionMenu(CTkBaseClass): | ||||
|                                height=self._apply_widget_scaling(self._desired_height)) | ||||
|         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): | ||||
|         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), | ||||
|   | ||||
| @@ -1,19 +1,332 @@ | ||||
| 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_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, | ||||
|                  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, | ||||
|                  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_disabled: Union[str, Tuple[str, str]] = "default_theme", | ||||
|  | ||||
|                  values: list = None): | ||||
|         super().__init__(master=master, ) | ||||
|                  values: list = None, | ||||
|                  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}'") | ||||
|  | ||||
|   | ||||
| @@ -102,30 +102,78 @@ class CTkBaseClass(tkinter.Frame): | ||||
|             return False | ||||
|  | ||||
|     def destroy(self): | ||||
|         """ Destroy this and all descendants widgets. """ | ||||
|         AppearanceModeTracker.remove(self._set_appearance_mode) | ||||
|         super().destroy() | ||||
|  | ||||
|     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} | ||||
|         return super().place(**self._apply_argument_scaling(kwargs)) | ||||
|  | ||||
|     def place_forget(self): | ||||
|         """ Unmap this widget. """ | ||||
|         self._last_geometry_manager_call = None | ||||
|         return super().place_forget() | ||||
|  | ||||
|     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} | ||||
|         return super().pack(**self._apply_argument_scaling(kwargs)) | ||||
|  | ||||
|     def pack_forget(self): | ||||
|         """ Unmap this widget and do not use it for the packing order. """ | ||||
|         self._last_geometry_manager_call = None | ||||
|         return super().pack_forget() | ||||
|  | ||||
|     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} | ||||
|         return super().grid(**self._apply_argument_scaling(kwargs)) | ||||
|  | ||||
|     def grid_forget(self): | ||||
|         """ Unmap this widget. """ | ||||
|         self._last_geometry_manager_call = None | ||||
|         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.") | ||||
|  | ||||
|     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: | ||||
|             new_bg_color = kwargs.pop("bg_color") | ||||
|   | ||||
| @@ -98,17 +98,22 @@ class App(customtkinter.CTk): | ||||
|         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_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.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.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.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.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.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 | ||||
|         self.sidebar_button_3.configure(state="disabled", text="Disabled CTkButton") | ||||
|   | ||||
							
								
								
									
										72
									
								
								test/manual_integration_tests/test_segmented_button.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								test/manual_integration_tests/test_segmented_button.py
									
									
									
									
									
										Normal 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() | ||||
		Reference in New Issue
	
	Block a user
	 Tom Schimansky
					Tom Schimansky