From 466ba7747eac416b5d0463f75e8d535885beff62 Mon Sep 17 00:00:00 2001 From: Tom Schimansky Date: Mon, 10 Oct 2022 00:48:08 +0200 Subject: [PATCH] worked on CTkTabview, fixed small issues --- CHANGELOG.md | 3 + customtkinter/__init__.py | 1 + customtkinter/assets/themes/blue.json | 4 +- customtkinter/widgets/ctk_entry.py | 2 +- customtkinter/widgets/ctk_frame.py | 37 ++- customtkinter/widgets/ctk_optionmenu.py | 2 +- customtkinter/widgets/ctk_segmented_button.py | 88 +++++-- customtkinter/widgets/ctk_slider.py | 2 +- customtkinter/widgets/ctk_tabview.py | 229 +++++++++++++----- customtkinter/widgets/ctk_textbox.py | 22 +- customtkinter/widgets/widget_base_class.py | 10 +- .../complex_example_new.py | 51 ++-- .../test_all_widgets_with_colors.py | 20 +- .../test_askdialog.py | 27 +-- .../test_segmented_button.py | 39 +-- test/manual_integration_tests/test_tabview.py | 16 +- 16 files changed, 375 insertions(+), 178 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35e48bf..25bb10b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,11 +8,14 @@ ToDo: - remove bg and background support for CTk and CTkToplevel (to be done) - enforce font size in pixel - enforce font to be tuple + - complete other theme files + - auto scaling of images ## Unreleased - 2022-10-2 ### Added - added .cget() method to all widgets and windows - added CTkTextbox with automatic x and y scrollbars, corner_radius, border_width, border_spacing + - added CTkSegmentedButton ### Changed - changed 'text_font' attribute to 'font' in all widgets diff --git a/customtkinter/__init__.py b/customtkinter/__init__.py index 0294366..742a9a7 100644 --- a/customtkinter/__init__.py +++ b/customtkinter/__init__.py @@ -4,6 +4,7 @@ import os import sys from tkinter.constants import * from tkinter import StringVar, IntVar, DoubleVar, BooleanVar +from tkinter import filedialog # import manager classes from .settings import Settings diff --git a/customtkinter/assets/themes/blue.json b/customtkinter/assets/themes/blue.json index 7154321..e28dbf9 100644 --- a/customtkinter/assets/themes/blue.json +++ b/customtkinter/assets/themes/blue.json @@ -14,9 +14,9 @@ "frame_high": ["#C0C2C5", "#343638"], "label": [null, null], "text": ["gray10", "#DCE4EE"], - "text_disabled": ["gray60", "#777B80"], + "text_disabled": ["gray60", "gray45"], "text_button": ["#DCE4EE", "#DCE4EE"], - "text_button_disabled": ["gray74", "gray74"], + "text_button_disabled": ["gray74", "gray60"], "progressbar": ["#939BA2", "#4A4D50"], "progressbar_progress": ["#3B8ED0", "#1F6AA5"], "progressbar_border": ["gray", "gray"], diff --git a/customtkinter/widgets/ctk_entry.py b/customtkinter/widgets/ctk_entry.py index 72c317a..45a4974 100644 --- a/customtkinter/widgets/ctk_entry.py +++ b/customtkinter/widgets/ctk_entry.py @@ -37,7 +37,7 @@ class CTkEntry(CTkBaseClass): textvariable: tkinter.Variable = None, placeholder_text: str = None, - font: Union[str, Tuple[str, str]] = "default_theme", + font: Union[str, Tuple] = "default_theme", state: str = tkinter.NORMAL, **kwargs): diff --git a/customtkinter/widgets/ctk_frame.py b/customtkinter/widgets/ctk_frame.py index c119246..27e0b1b 100644 --- a/customtkinter/widgets/ctk_frame.py +++ b/customtkinter/widgets/ctk_frame.py @@ -24,6 +24,7 @@ class CTkFrame(CTkBaseClass): bg_color: Union[str, Tuple[str, str], None] = None, fg_color: Union[str, Tuple[str, str], None] = "default_theme", border_color: Union[str, Tuple[str, str]] = "default_theme", + background_corner_colors: Tuple[Union[str, Tuple[str, str]]] = None, overwrite_preferred_drawing_method: str = None, **kwargs): @@ -46,6 +47,8 @@ class CTkFrame(CTkBaseClass): else: self._fg_color = fg_color + self._background_corner_colors = background_corner_colors # rendering options for DrawEngine + # shape self._corner_radius = ThemeManager.theme["shape"]["frame_corner_radius"] if corner_radius == "default_theme" else corner_radius self._border_width = ThemeManager.theme["shape"]["frame_border_width"] if border_width == "default_theme" else border_width @@ -90,10 +93,19 @@ class CTkFrame(CTkBaseClass): self._draw() def _draw(self, no_color_updates=False): - if not self._canvas.winfo_exists(): return + 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), @@ -115,29 +127,34 @@ class CTkFrame(CTkBaseClass): outline=ThemeManager.single_color(self._border_color, self._appearance_mode)) self._canvas.configure(bg=ThemeManager.single_color(self._bg_color, self._appearance_mode)) - self._canvas.tag_lower("inner_parts") - self._canvas.tag_lower("border_parts") + # self._canvas.tag_lower("inner_parts") # maybe unnecessary, I don't know ??? + # self._canvas.tag_lower("border_parts") def configure(self, require_redraw=False, **kwargs): if "fg_color" in kwargs: self._fg_color = kwargs.pop("fg_color") require_redraw = True - # check if CTk widgets are children of the frame and change their _bg_color to new frame fg_color + # check if CTk widgets are children of the frame and change their bg_color to new frame fg_color for child in self.winfo_children(): 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 "bg_color" in kwargs: + # pass bg_color change to children if fg_color is None + if self._fg_color is None: + for child in self.winfo_children(): + if isinstance(child, CTkBaseClass): + child.configure(bg_color=self._fg_color) if "border_color" in kwargs: self._border_color = kwargs.pop("border_color") require_redraw = True + if "background_corner_colors" in kwargs: + self._background_corner_colors = kwargs.pop("background_corner_colors") + require_redraw = True + if "corner_radius" in kwargs: self._corner_radius = kwargs.pop("corner_radius") require_redraw = True @@ -164,6 +181,8 @@ class CTkFrame(CTkBaseClass): return self._fg_color elif attribute_name == "border_color": return self._border_color + elif attribute_name == "background_corner_colors": + return self._background_corner_colors else: return super().cget(attribute_name) diff --git a/customtkinter/widgets/ctk_optionmenu.py b/customtkinter/widgets/ctk_optionmenu.py index 5fdd89a..1374e52 100644 --- a/customtkinter/widgets/ctk_optionmenu.py +++ b/customtkinter/widgets/ctk_optionmenu.py @@ -38,7 +38,7 @@ class CTkOptionMenu(CTkBaseClass): variable: tkinter.Variable = None, state: str = tkinter.NORMAL, hover: bool = True, - command: Callable = None, + command: Callable[[str], None] = None, dynamic_resizing: bool = True, **kwargs): diff --git a/customtkinter/widgets/ctk_segmented_button.py b/customtkinter/widgets/ctk_segmented_button.py index 36657f2..7cda0d8 100644 --- a/customtkinter/widgets/ctk_segmented_button.py +++ b/customtkinter/widgets/ctk_segmented_button.py @@ -1,5 +1,5 @@ import tkinter -from typing import Union, Tuple, List, Dict +from typing import Union, Tuple, List, Dict, Callable from ..theme_manager import ThemeManager from .ctk_button import CTkButton @@ -27,10 +27,14 @@ class CTkSegmentedButton(CTkFrame): 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", + background_corner_colors: Tuple[Union[str, Tuple[str, str]]] = None, + font: any = "default_theme", values: list = None, variable: tkinter.Variable = None, dynamic_resizing: bool = True, + command: Callable[[str], None] = None, + state: str = "normal", **kwargs): super().__init__(master=master, bg_color=bg_color, width=width, height=height, **kwargs) @@ -49,11 +53,15 @@ class CTkSegmentedButton(CTkFrame): 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._background_corner_colors = background_corner_colors # rendering options for DrawEngine + + self._command: Callable[[str], None] = command + self._font = (ThemeManager.theme["text"]["font"], ThemeManager.theme["text"]["size"]) if font == "default_theme" else font + self._state = state + 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 @@ -63,8 +71,9 @@ class CTkSegmentedButton(CTkFrame): self._check_unique_values(self._value_list) self._current_value: str = "" - self._create_buttons_from_values() - self._create_button_grid() + if len(self._value_list) > 0: + self._create_buttons_from_values() + self._create_button_grid() self._variable = variable self._variable_callback_blocked: bool = False @@ -74,7 +83,7 @@ class CTkSegmentedButton(CTkFrame): 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) + super().configure(corner_radius=self._sb_corner_radius, fg_color=None) def destroy(self): if self._variable is not None: # remove old callback @@ -95,11 +104,23 @@ class CTkSegmentedButton(CTkFrame): 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)) + if self._background_corner_colors is None: + self._buttons_dict[self._value_list[index]].configure(background_corner_colors=(self._bg_color, self._bg_color, self._bg_color, self._bg_color)) + else: + self._buttons_dict[self._value_list[index]].configure(background_corner_colors=self._background_corner_colors) + 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)) + if self._background_corner_colors is None: + 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)) + else: + self._buttons_dict[self._value_list[index]].configure(background_corner_colors=(self._background_corner_colors[0], self._sb_fg_color, self._sb_fg_color, self._background_corner_colors[3])) + 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)) + if self._background_corner_colors is None: + 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._background_corner_colors[1], self._background_corner_colors[2], 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)) @@ -129,7 +150,9 @@ class CTkSegmentedButton(CTkFrame): 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), + font=self._font, + state=self._state, + command=lambda v=value: self.set(v, from_button_callback=True), background_corner_colors=None, round_width_to_even_numbers=False) # DrawEngine rendering option (so that theres no gap between buttons) @@ -212,19 +235,30 @@ class CTkSegmentedButton(CTkFrame): for button in self._buttons_dict.values(): button.configure(text_color_disabled=self._sb_text_color_disabled) + if "background_corner_colors" in kwargs: + self._background_corner_colors = kwargs.pop("background_corner_colors") + for i in range(len(self._buttons_dict)): + self._configure_button_corners_for_index(i) + + if "font" in kwargs: + self._font = kwargs.pop("font") + for button in self._buttons_dict.values(): + button.configure(font=self._font) + 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 len(self._value_list) > 0: + self._create_buttons_from_values() + self._create_button_grid() + + if self._current_value in self._value_list: + self._select_button_by_value(self._current_value) if "variable" in kwargs: if self._variable is not None: # remove old callback @@ -245,6 +279,14 @@ class CTkSegmentedButton(CTkFrame): else: self.grid_propagate(True) + if "command" in kwargs: + self._command = kwargs.pop("command") + + if "state" in kwargs: + self._state = kwargs.pop("state") + for button in self._buttons_dict.values(): + button.configure(state=self._state) + super().configure(**kwargs) def cget(self, attribute_name: str) -> any: @@ -268,17 +310,25 @@ class CTkSegmentedButton(CTkFrame): elif attribute_name == "text_color_disabled": return self._sb_text_color_disabled + elif attribute_name == "font": + return self._font elif attribute_name == "values": return self._value_list elif attribute_name == "variable": return self._variable elif attribute_name == "dynamic_resizing": return self._dynamic_resizing + elif attribute_name == "command": + return self._command else: return super().cget(attribute_name) - def set(self, value: str, from_variable_callback: bool = False): + def set(self, value: str, from_variable_callback: bool = False, from_button_callback: bool = False): + if from_button_callback: + if self._command is not None: + self._command(self._current_value) + if value == self._current_value: return elif value in self._buttons_dict: @@ -301,7 +351,7 @@ class CTkSegmentedButton(CTkFrame): def get(self) -> str: return self._current_value - def insert_value(self, index: int, value: str): + def insert(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) @@ -319,7 +369,7 @@ class CTkSegmentedButton(CTkFrame): else: raise ValueError(f"CTkSegmentedButton can not insert value '{value}', already part of the values") - def remove_value(self, value: str): + def delete(self, value: str): if value in self._buttons_dict: self._buttons_dict[value].destroy() self._buttons_dict.pop(value) diff --git a/customtkinter/widgets/ctk_slider.py b/customtkinter/widgets/ctk_slider.py index 3de5eb3..c3e4f9d 100644 --- a/customtkinter/widgets/ctk_slider.py +++ b/customtkinter/widgets/ctk_slider.py @@ -35,7 +35,7 @@ class CTkSlider(CTkBaseClass): to: int = 1, state: str = "normal", number_of_steps: Union[int, None] = None, - command: Callable = None, + command: Callable[[float], None] = None, variable: tkinter.Variable = None, orientation: str = "horizontal", **kwargs): diff --git a/customtkinter/widgets/ctk_tabview.py b/customtkinter/widgets/ctk_tabview.py index 0d788f6..6e15edd 100644 --- a/customtkinter/widgets/ctk_tabview.py +++ b/customtkinter/widgets/ctk_tabview.py @@ -1,18 +1,11 @@ -import tkinter -from typing import Union, Tuple, List +from typing import Union, Tuple, Dict, List from ..theme_manager import ThemeManager -from .widget_base_class import CTkBaseClass from .ctk_frame import CTkFrame - - -class CTkTab: - def __init__(self, master=None, identifier: str = None, text: str = "CTkTab", index: int = 0): - self.text: str = text - self.frame: tkinter.Frame = tkinter.Frame(master, width=0, height=0) - self.identifier = str(id(self.frame)) if identifier is None else identifier - self.visible: bool = True - self.index = index +from .widget_base_class import CTkBaseClass +from .ctk_segmented_button import CTkSegmentedButton +from .ctk_canvas import CTkCanvas +from ..draw_engine import DrawEngine class CTkTabview(CTkBaseClass): @@ -23,7 +16,7 @@ class CTkTabview(CTkBaseClass): _top_spacing = 10 # px on top of the buttons _top_button_overhang = 8 # px - _button_size = 24 + _button_height = 10 def __init__(self, master: any = None, @@ -34,18 +27,26 @@ class CTkTabview(CTkBaseClass): bg_color: Union[str, Tuple[str, str], None] = None, fg_color: Union[str, Tuple[str, str], None] = "default_theme", - - button_frame_color: Union[str, Tuple[str, str]] = "default_theme", - button_color: Union[str, Tuple[str, str]] = "default_theme", - button_hover_color: Union[str, Tuple[str, str]] = "default_theme", border_color: Union[str, Tuple[str, str]] = "default_theme", + segmented_button_fg_color: Union[str, Tuple[str, str], None] = "default_theme", + segmented_button_selected_color: Union[str, Tuple[str, str]] = "default_theme", + segmented_button_selected_hover_color: Union[str, Tuple[str, str]] = "default_theme", + segmented_button_unselected_color: Union[str, Tuple[str, str]] = "default_theme", + segmented_button_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", + **kwargs): - # transfer basic functionality (bg_color, size, appearance_mode, scaling) to CTkBaseClass + # transfer some functionality to CTkFrame super().__init__(master=master, bg_color=bg_color, width=width, height=height, **kwargs) - # determine fg_color + # color + self._border_color = ThemeManager.theme["color"]["frame_border"] if border_color == "default_theme" else border_color + + # determine fg_color of frame if fg_color == "default_theme": if isinstance(self.master, (CTkFrame, CTkTabview)): if self.master.cget("fg_color") == ThemeManager.theme["color"]["frame_low"]: @@ -57,65 +58,169 @@ class CTkTabview(CTkBaseClass): else: self._fg_color = fg_color - self._border_color = ThemeManager.theme["color"]["frame_border"] if border_color == "default_theme" else border_color - self._button_frame_color = ThemeManager.theme["color"]["tabview_button_frame"] if button_frame_color == "default_theme" else button_frame_color - self._button_color = ThemeManager.theme["color"]["tabview_button"] if button_color == "default_theme" else button_color - self._button_hover_color = ThemeManager.theme["color"]["tabview_button_hover"] if button_hover_color == "default_theme" else button_hover_color - # shape self._corner_radius = ThemeManager.theme["shape"]["frame_corner_radius"] if corner_radius == "default_theme" else corner_radius self._border_width = ThemeManager.theme["shape"]["frame_border_width"] if border_width == "default_theme" else border_width - self._main_frame = CTkFrame(self, - width=width, - height=height - (self._top_spacing + self._top_button_overhang), - bg_color=self._bg_color, - fg_color=self._fg_color, - border_color=self._border_color, - border_width=self._border_width) - self._button_frame = CTkFrame(self, - width=0, - height=0, - bg_color=self._fg_color, - fg_color=self._button_frame_color, - border_color=self._border_color, - border_width=self._border_width) - self._create_grid_for_frames() + self._canvas = CTkCanvas(master=self, + bg=ThemeManager.single_color(self._bg_color, self._appearance_mode), + highlightthickness=0, + width=self._apply_widget_scaling(self._current_width - self._top_spacing - self._top_button_overhang), + height=self._apply_widget_scaling(self._current_height)) + self._draw_engine = DrawEngine(self._canvas) - self._tab_list: List[CTkTab] = [] + self._segmented_button = CTkSegmentedButton(self, + values=[], + height=self._button_height, + fg_color=segmented_button_fg_color, + selected_color=segmented_button_selected_color, + selected_hover_color=segmented_button_selected_hover_color, + unselected_color=segmented_button_unselected_color, + unselected_hover_color=segmented_button_unselected_hover_color, + text_color=text_color, + text_color_disabled=text_color_disabled, + corner_radius=corner_radius, + border_width=self._apply_widget_scaling(3)) + self._configure_segmented_button_background_corners() + self._configure_grid() + self._set_grid_canvas() - def _create_grid_for_frames(self): + self._tab_dict: Dict[str, CTkFrame] = {} + self._name_list: List[str] = [] # list of unique tab names in order of tabs + self._current_name: str = "" + + super().bind('', self._update_dimensions_event) + + def winfo_children(self) -> List[any]: + """ + winfo_children of CTkTabview without canvas and segmented button widgets, + because it's not a child but part of the CTkTabview itself + """ + + child_widgets = super().winfo_children() + try: + child_widgets.remove(self._canvas) + child_widgets.remove(self._segmented_button) + return child_widgets + except ValueError: + return child_widgets + + def _set_scaling(self, *args, **kwargs): + super()._set_scaling(*args, **kwargs) + + self._canvas.configure(width=self._apply_widget_scaling(self._desired_width), + height=self._apply_widget_scaling(self._desired_height - self._top_spacing - self._top_button_overhang)) + self._draw() + + def _set_dimensions(self, width=None, height=None): + super()._set_dimensions(width, height) + + self._canvas.configure(width=self._apply_widget_scaling(self._desired_width), + height=self._apply_widget_scaling(self._desired_height - self._top_spacing - self._top_button_overhang)) + self._draw() + + def _configure_segmented_button_background_corners(self): + """ needs to be called for changes in fg_color, bg_color """ + + if self._fg_color is not None: + self._segmented_button.configure(background_corner_colors=(self._bg_color, self._bg_color, self._fg_color, self._fg_color)) + else: + self._segmented_button.configure(background_corner_colors=(self._bg_color, self._bg_color, self._bg_color, self._bg_color)) + + def _configure_tab_background_corners_by_name(self, name: str): + """ needs to be called for changes in fg_color, bg_color, border_width """ + + if self._border_width == 0: + if self._fg_color is not None: + self._tab_dict[name].configure(background_corner_colors=(self._fg_color, self._fg_color, self._bg_color, self._bg_color)) + else: + self._tab_dict[name].configure(background_corner_colors=(self._bg_color, self._bg_color, self._bg_color, self._bg_color)) + else: + self._tab_dict[name].configure(background_corner_colors=None) + + def _configure_grid(self): """ create 3 x 4 grid system """ self.grid_rowconfigure(0, weight=0, minsize=self._apply_widget_scaling(self._top_spacing)) self.grid_rowconfigure(1, weight=0, minsize=self._apply_widget_scaling(self._top_button_overhang)) - self.grid_rowconfigure(2, weight=0, minsize=self._apply_widget_scaling(self._button_size - self._top_button_overhang)) + self.grid_rowconfigure(2, weight=0, minsize=self._apply_widget_scaling(self._button_height - self._top_button_overhang)) self.grid_rowconfigure(3, weight=1) - self.grid_columnconfigure((0, 2), weight=1, minsize=self._apply_widget_scaling(self._corner_radius)) - self.grid_columnconfigure(1, weight=0, minsize=self._apply_widget_scaling(self._corner_radius)) + self.grid_columnconfigure(0, weight=1) - self._main_frame.grid(row=2, column=0, rowspan=2, columnspan=3, sticky="nsew") - self._button_frame.grid(row=1, column=1, rowspan=2, columnspan=1, sticky="nsew") + def _set_grid_canvas(self): + self._canvas.grid(row=2, rowspan=2, column=0, columnspan=1, sticky="nsew") - def _get_tab_by_identifier(self, identifier: str): - for tab in self._tab_list: - if tab.identifier == identifier: - return tab + def _set_grid_segmented_button(self): + """ needs to be called for changes in corner_radius """ + self._segmented_button.grid(row=1, rowspan=2, column=0, columnspan=1, padx=self._apply_widget_scaling(self._corner_radius), sticky="ns") - def create_tab(self, identifier=None, text="CTkTabview"): - new_tab = CTkTab(master=self, identifier=identifier, text=text) - self._tab_list.append(new_tab) - return new_tab.identifier + def _set_grid_tab_by_name(self, name: str): + """ needs to be called for changes in corner_radius, border_width """ + if self._border_width == 0: + self._tab_dict[name].grid(row=3, column=0, sticky="nsew") + else: + self._tab_dict[name].grid(row=3, column=0, sticky="nsew", + padx=self._apply_widget_scaling(max(self._corner_radius, self._border_width)), + pady=self._apply_widget_scaling(max(self._corner_radius, self._border_width))) - def select_tab(self, identifier: str): - selected_tab = self._get_tab_by_identifier(identifier) - for tab in self._tab_list: - if tab != selected_tab: - tab.frame.grid_forget() + def _create_tab(self) -> CTkFrame: + new_tab = CTkFrame(self, + fg_color=self._fg_color, + border_width=0, + corner_radius=self._corner_radius) + return new_tab - selected_tab.frame.grid(row=3, column=0, rowspan=1, columnspan=3, sticky="nsew") + def _draw(self, no_color_updates: bool = False): + if not self._canvas.winfo_exists(): + return - def get_tab(self, identifier): - pass + 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._top_spacing - self._top_button_overhang), + self._apply_widget_scaling(self._corner_radius), + self._apply_widget_scaling(self._border_width)) + if no_color_updates is False or requires_recoloring: + if self._fg_color is None: + self._canvas.itemconfig("inner_parts", + fill=ThemeManager.single_color(self._bg_color, self._appearance_mode), + outline=ThemeManager.single_color(self._bg_color, self._appearance_mode)) + else: + self._canvas.itemconfig("inner_parts", + fill=ThemeManager.single_color(self._fg_color, self._appearance_mode), + outline=ThemeManager.single_color(self._fg_color, self._appearance_mode)) + + self._canvas.itemconfig("border_parts", + fill=ThemeManager.single_color(self._border_color, self._appearance_mode), + outline=ThemeManager.single_color(self._border_color, self._appearance_mode)) + self._canvas.configure(bg=ThemeManager.single_color(self._bg_color, self._appearance_mode)) + + def tab(self, name: str) -> CTkFrame: + """ returns reference to the tab with given name """ + + if name in self._tab_dict: + return self._tab_dict[name] + else: + raise ValueError(f"CTkTabview has no tab named '{name}'") + + def insert(self, index: int, name: str): + """ creates new tab with given name at position index """ + + if name not in self._tab_dict: + # if no tab exists, set grid for segmented button + if len(self._tab_dict) == 0: + self._set_grid_segmented_button() + + self._name_list.insert(index, name) + self._tab_dict[name] = self._create_tab() + self._segmented_button.insert(index, name) + else: + raise ValueError(f"CTkTabview already has tab named '{name}'") + + def add(self, name: str): + """ appends new tab with given name """ + self.insert(len(self._tab_dict), name) + + def delete(self, name: str): + """ deletes tab with given name """ + return diff --git a/customtkinter/widgets/ctk_textbox.py b/customtkinter/widgets/ctk_textbox.py index ec705ef..142fbf4 100644 --- a/customtkinter/widgets/ctk_textbox.py +++ b/customtkinter/widgets/ctk_textbox.py @@ -104,9 +104,9 @@ class CTkTextbox(CTkBaseClass): orientation="vertical", command=self._textbox.yview) self._textbox.configure(yscrollcommand=self._y_scrollbar.set) - self._y_scrollbar.grid(row=0, column=1, rowspan=1, columnspan=1, sticky="ns", - padx=(self._apply_widget_scaling(3), self._apply_widget_scaling(self._border_spacing + self._border_width)), - pady=(self._apply_widget_scaling(self._corner_radius + self._border_width), 0)) + #self._y_scrollbar.grid(row=0, column=1, rowspan=1, columnspan=1, sticky="ns", + # padx=(self._apply_widget_scaling(3), self._apply_widget_scaling(self._border_spacing + self._border_width)), + # pady=(self._apply_widget_scaling(self._corner_radius + self._border_width), 0)) self._x_scrollbar = CTkScrollbar(self, height=8, @@ -118,9 +118,9 @@ class CTkTextbox(CTkBaseClass): orientation="horizontal", command=self._textbox.xview) self._textbox.configure(xscrollcommand=self._x_scrollbar.set) - self._x_scrollbar.grid(row=1, column=0, rowspan=1, columnspan=1, sticky="ew", - pady=(self._apply_widget_scaling(3), self._apply_widget_scaling(self._border_spacing + self._border_width)), - padx=(self._apply_widget_scaling(self._corner_radius + self._border_width), 0)) + #self._x_scrollbar.grid(row=1, column=0, rowspan=1, columnspan=1, sticky="ew", + # pady=(self._apply_widget_scaling(3), self._apply_widget_scaling(self._border_spacing + self._border_width)), + # padx=(self._apply_widget_scaling(self._corner_radius + self._border_width), 0)) self._create_grid_for_text_and_scrollbars(re_grid_textbox=True, re_grid_x_scrollbar=True, re_grid_y_scrollbar=True) @@ -269,6 +269,11 @@ class CTkTextbox(CTkBaseClass): self._create_grid_for_text_and_scrollbars(re_grid_textbox=True, re_grid_x_scrollbar=True, re_grid_y_scrollbar=True) require_redraw = True + if "border_spacing" in kwargs: + self._border_spacing = kwargs.pop("border_spacing") + self._create_grid_for_text_and_scrollbars(re_grid_textbox=True, re_grid_x_scrollbar=True, re_grid_y_scrollbar=True) + require_redraw = True + if "width" in kwargs: self._set_dimensions(width=kwargs.pop("width")) @@ -287,6 +292,8 @@ class CTkTextbox(CTkBaseClass): return self._corner_radius elif attribute_name == "border_width": return self._border_width + elif attribute_name == "border_spacing": + return self._border_spacing elif attribute_name == "fg_color": return self._fg_color @@ -336,6 +343,9 @@ class CTkTextbox(CTkBaseClass): def compare(self, index, op, index2): return self._textbox.compare(index, op, index2) + def delete(self, index1, index2=None): + return self._textbox.delete(index1, index2) + def dlineinfo(self, index): return self._textbox.dlineinfo(index) diff --git a/customtkinter/widgets/widget_base_class.py b/customtkinter/widgets/widget_base_class.py index 8da92c5..9289d10 100644 --- a/customtkinter/widgets/widget_base_class.py +++ b/customtkinter/widgets/widget_base_class.py @@ -2,7 +2,7 @@ import tkinter import tkinter.ttk as ttk import copy import re -from typing import Union, Callable +from typing import Union, Callable, Tuple try: from typing import TypedDict @@ -30,7 +30,7 @@ class CTkBaseClass(tkinter.Frame): width: int = 0, height: int = 0, - bg_color: Union[str, tuple] = None, + bg_color: Union[str, Tuple[str, str], None] = None, **kwargs): super().__init__(master=master, width=width, height=height, **pop_from_dict_by_set(kwargs, self._valid_tk_frame_attributes)) @@ -64,7 +64,7 @@ class CTkBaseClass(tkinter.Frame): self._appearance_mode = AppearanceModeTracker.get_mode() # 0: "Light" 1: "Dark" # background color - self._bg_color = self._detect_color_of_master() if bg_color is None else bg_color + self._bg_color: Union[str, Tuple[str, str]] = self._detect_color_of_master() if bg_color is None else bg_color super().configure(bg=ThemeManager.single_color(self._bg_color, self._appearance_mode)) @@ -104,6 +104,7 @@ class CTkBaseClass(tkinter.Frame): def destroy(self): """ Destroy this and all descendants widgets. """ AppearanceModeTracker.remove(self._set_appearance_mode) + ScalingTracker.remove_widget(self._set_scaling, self) super().destroy() def place(self, **kwargs): @@ -240,6 +241,7 @@ class CTkBaseClass(tkinter.Frame): raise ValueError(f"'{attribute_name}' is not a supported argument. Look at the documentation for supported arguments.") def _update_dimensions_event(self, event): + print(event) # only redraw if dimensions changed (for performance), independent of scaling if round(self._current_width) != round(event.width / self._widget_scaling) or round(self._current_height) != round(event.height / self._widget_scaling): self._current_width = (event.width / self._widget_scaling) # adjust current size according to new size given by event @@ -247,7 +249,7 @@ class CTkBaseClass(tkinter.Frame): self._draw(no_color_updates=True) # faster drawing without color changes - def _detect_color_of_master(self, master_widget=None): + def _detect_color_of_master(self, master_widget=None) -> Union[str, Tuple[str, str]]: """ detect color of self.master widget to set correct _bg_color """ if master_widget is None: diff --git a/test/manual_integration_tests/complex_example_new.py b/test/manual_integration_tests/complex_example_new.py index d9b49f8..36d168e 100644 --- a/test/manual_integration_tests/complex_example_new.py +++ b/test/manual_integration_tests/complex_example_new.py @@ -12,14 +12,14 @@ class App(customtkinter.CTk): super().__init__() self.title("CustomTkinter complex_example.py") - self.geometry(f"{920}x{500}") - self.minsize(700, 400) - self.maxsize(1200, 700) + self.geometry(f"{1100}x{580}") + self.minsize(800, 400) + self.maxsize(1300, 700) self.protocol("WM_DELETE_WINDOW", self.on_closing) # call .on_closing() when app gets closed # configure grid layout (4x4) self.grid_columnconfigure(1, weight=1) - self.grid_columnconfigure((2, 3), weight=0, minsize=200) + self.grid_columnconfigure((2, 3, 4), weight=0, minsize=200) self.grid_rowconfigure((0, 1, 2), weight=1) # create sidebar frame with widgets @@ -52,22 +52,10 @@ class App(customtkinter.CTk): self.main_button_1 = customtkinter.CTkButton(master=self, fg_color=None, border_width=2) self.main_button_1.grid(row=3, column=3, padx=(10, 20), pady=(10, 20), sticky="nsew") + # create textbox self.textbox = customtkinter.CTkTextbox(self) self.textbox.grid(row=0, column=1, padx=(20, 10), pady=(20, 10), sticky="nsew") - # create radiobutton frame - self.radiobutton_frame = customtkinter.CTkFrame(self) - self.radiobutton_frame.grid(row=0, column=3, padx=(10, 20), pady=(20, 10), sticky="nsew") - self.radio_var = tkinter.IntVar(value=0) - self.label_radio_group = customtkinter.CTkLabel(master=self.radiobutton_frame, text="CTkRadioButton Group:") - self.label_radio_group.grid(row=0, column=2, columnspan=1, padx=10, pady=10, sticky="") - self.radio_button_1 = customtkinter.CTkRadioButton(master=self.radiobutton_frame, variable=self.radio_var, value=0) - self.radio_button_1.grid(row=1, column=2, pady=10, padx=20, sticky="n") - self.radio_button_2 = customtkinter.CTkRadioButton(master=self.radiobutton_frame, variable=self.radio_var, value=1) - self.radio_button_2.grid(row=2, column=2, pady=10, padx=20, sticky="n") - self.radio_button_3 = customtkinter.CTkRadioButton(master=self.radiobutton_frame, variable=self.radio_var, value=2) - self.radio_button_3.grid(row=3, column=2, pady=10, padx=20, sticky="n") - # create optionemnu and combobox frame self.optionemnu_combobox_frame = customtkinter.CTkFrame(self) self.optionemnu_combobox_frame.grid(row=0, column=2, padx=(10, 10), pady=(20, 10), sticky="nsew") @@ -82,9 +70,22 @@ class App(customtkinter.CTk): command=self.open_input_dialog) self.string_input_button.grid(row=2, column=0, padx=20, pady=(10, 10), sticky="ew") + # create radiobutton frame + self.radiobutton_frame = customtkinter.CTkFrame(self) + self.radiobutton_frame.grid(row=0, column=3, padx=(10, 10), pady=(20, 10), sticky="nsew") + self.radio_var = tkinter.IntVar(value=0) + self.label_radio_group = customtkinter.CTkLabel(master=self.radiobutton_frame, text="CTkRadioButton Group:") + self.label_radio_group.grid(row=0, column=2, columnspan=1, padx=10, pady=10, sticky="") + self.radio_button_1 = customtkinter.CTkRadioButton(master=self.radiobutton_frame, variable=self.radio_var, value=0) + self.radio_button_1.grid(row=1, column=2, pady=10, padx=20, sticky="n") + self.radio_button_2 = customtkinter.CTkRadioButton(master=self.radiobutton_frame, variable=self.radio_var, value=1) + self.radio_button_2.grid(row=2, column=2, pady=10, padx=20, sticky="n") + self.radio_button_3 = customtkinter.CTkRadioButton(master=self.radiobutton_frame, variable=self.radio_var, value=2) + self.radio_button_3.grid(row=3, column=2, pady=10, padx=20, sticky="n") + # create checkbox and switch frame self.checkbox_slider_frame = customtkinter.CTkFrame(self) - self.checkbox_slider_frame.grid(row=1, column=3, padx=(10, 20), pady=(10, 10), sticky="nsew") + self.checkbox_slider_frame.grid(row=0, column=4, padx=(10, 20), pady=(20, 10), sticky="nsew") self.checkbox_1 = customtkinter.CTkCheckBox(master=self.checkbox_slider_frame) self.checkbox_1.grid(row=1, column=0, pady=(20, 10), padx=20, sticky="n") self.checkbox_2 = customtkinter.CTkCheckBox(master=self.checkbox_slider_frame) @@ -100,9 +101,8 @@ class App(customtkinter.CTk): self.slider_progressbar_frame.grid_columnconfigure(0, weight=1) self.slider_progressbar_frame.grid_rowconfigure(4, weight=1) - self.seg_button = customtkinter._CTkSegmentedButton(self.slider_progressbar_frame, - values=["CTkSegmentedButton", "Value 2", "Value 3"]) - self.seg_button.grid(row=0, column=0, padx=(20, 10), pady=(10, 10), sticky="ew") + self.seg_button_1 = customtkinter._CTkSegmentedButton(self.slider_progressbar_frame) + self.seg_button_1.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=1, column=0, padx=(20, 10), pady=(10, 10), sticky="ew") @@ -115,6 +115,10 @@ class App(customtkinter.CTk): self.progressbar_3 = customtkinter.CTkProgressBar(self.slider_progressbar_frame, orientation="vertical") self.progressbar_3.grid(row=0, column=2, rowspan=5, padx=(10, 20), pady=(10, 10), sticky="ns") + # create tabview + self.tabview = customtkinter._CTkTabview(self) + self.tabview.grid(row=1, column=3, columnspan=2, padx=(10, 20), pady=(10, 10), sticky="nsew") + # set default values self.sidebar_button_3.configure(state="disabled", text="Disabled CTkButton") self.checkbox_2.configure(state="disabled") @@ -130,8 +134,11 @@ class App(customtkinter.CTk): self.slider_2.configure(command=self.progressbar_3.set) self.progressbar_1.configure(mode="indeterminnate") self.progressbar_1.start() + self.textbox.insert("0.0", "CTkTextbox\n\n" + "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.\n\n" * 20) + self.seg_button_1.configure(values=["CTkSegmentedButton", "Value 2", "Value 3"]) + self.seg_button_1.set("Value 2") - self.textbox.insert("1.0", "CTkTextbox\n\n" + "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.\n\n" * 20) + self.radiobutton_frame.configure(border_width=50) def open_input_dialog(self): dialog = customtkinter.CTkInputDialog(master=self, text="Type in a number:", title="CTkInputDialog") diff --git a/test/manual_integration_tests/test_all_widgets_with_colors.py b/test/manual_integration_tests/test_all_widgets_with_colors.py index 1bc58b6..acf75a7 100644 --- a/test/manual_integration_tests/test_all_widgets_with_colors.py +++ b/test/manual_integration_tests/test_all_widgets_with_colors.py @@ -2,7 +2,7 @@ import tkinter import customtkinter customtkinter.set_appearance_mode("System") # Other: "Dark", "Light" -customtkinter.set_default_color_theme("green") # Themes: "blue" (standard), "green", "dark-blue" +# customtkinter.set_default_color_theme("green") # Themes: "blue" (standard), "green", "dark-blue" class TestApp(customtkinter.CTk): @@ -20,13 +20,13 @@ class TestApp(customtkinter.CTk): """ gets called by self.slider_1 """ if value == 0: - self.label_1.set_text("mode: Light") + self.label_1.configure(text="mode: Light") customtkinter.set_appearance_mode("Light") elif value == 1: - self.label_1.set_text("mode: Dark") + self.label_1.configure(text="mode: Dark") customtkinter.set_appearance_mode("Dark") else: - self.label_1.set_text("mode: System") + self.label_1.configure(text="mode: System") customtkinter.set_appearance_mode("System") def create_widgets_on_tk(self): @@ -74,7 +74,7 @@ class TestApp(customtkinter.CTk): self.progress_bar_2 = customtkinter.CTkProgressBar(master=self.ctk_frame) self.progress_bar_2.place(relx=0.5, y=y + 320, anchor=tkinter.CENTER) - self.slider_2 = customtkinter.CTkSlider(master=self.ctk_frame, command=lambda v: self.label_2.set_text(str(round(v, 5)))) + self.slider_2 = customtkinter.CTkSlider(master=self.ctk_frame, command=lambda v: self.label_2.configure(text=str(round(v, 5)))) self.slider_2.place(relx=0.5, y=y + 400, anchor=tkinter.CENTER) self.check_box_2 = customtkinter.CTkCheckBox(master=self.ctk_frame) @@ -102,7 +102,7 @@ class TestApp(customtkinter.CTk): self.ctk_frame_customized.configure(fg_color=("#F4F4FA", "#1E2742")) self.label_3 = customtkinter.CTkLabel(master=self.ctk_frame_customized, text="customized", corner_radius=60, - text_font=("times", 16)) + font=("times", 16)) self.label_3.place(relx=0.5, y=y, anchor=tkinter.CENTER) self.label_3.configure(fg_color=("#F4F4FA", "#333D5E"), text_color=("#373E57", "#7992C1")) @@ -111,12 +111,12 @@ class TestApp(customtkinter.CTk): self.frame_3.configure(fg_color=("#EBECF3", "#4B577E")) self.button_3 = customtkinter.CTkButton(master=self.ctk_frame_customized, command=lambda: x, border_width=3, - corner_radius=20, text_font=("times", 16)) + corner_radius=20, font=("times", 16)) self.button_3.place(relx=0.5, y=y + 160, anchor=tkinter.CENTER) self.button_3.configure(border_color=("#4F90F8", "#6FADF9"), hover_color=("#3A65E8", "#4376EE")) self.button_3.configure(fg_color=None) - self.entry_3 = customtkinter.CTkEntry(master=self.ctk_frame_customized, text_font=("times", 16)) + self.entry_3 = customtkinter.CTkEntry(master=self.ctk_frame_customized, font=("times", 16)) self.entry_3.place(relx=0.5, y=y + 240, anchor=tkinter.CENTER) self.entry_3.configure(fg_color=("gray60", "gray5"), corner_radius=20) self.entry_3.insert(0, "1234567890") @@ -131,7 +131,7 @@ class TestApp(customtkinter.CTk): self.slider_3.configure(button_color="#8AE0C3", fg_color=("#EBECF3", "#4B577E"), progress_color=("gray30", "gray10")) self.slider_3.configure(from_=0, to=1) - self.check_box_3 = customtkinter.CTkCheckBox(master=self.ctk_frame_customized, corner_radius=50, text_font=("times", 16)) + self.check_box_3 = customtkinter.CTkCheckBox(master=self.ctk_frame_customized, corner_radius=50, font=("times", 16)) self.check_box_3.place(relx=0.5, y=y + 480, anchor=tkinter.CENTER) self.check_box_3.configure(border_color="#8AE0C3") @@ -176,4 +176,4 @@ class TestApp(customtkinter.CTk): if __name__ == "__main__": test_app = TestApp() - test_app.mainloop() \ No newline at end of file + test_app.mainloop() diff --git a/test/manual_integration_tests/test_askdialog.py b/test/manual_integration_tests/test_askdialog.py index 5ab7924..e8bc19f 100644 --- a/test/manual_integration_tests/test_askdialog.py +++ b/test/manual_integration_tests/test_askdialog.py @@ -1,6 +1,5 @@ import tkinter import tkinter.messagebox -from tkinter import filedialog as fd import customtkinter @@ -23,35 +22,31 @@ class App(customtkinter.CTk): self.minsize(App.WIDTH, App.HEIGHT) self.protocol("WM_DELETE_WINDOW", self.on_closing) - # ============ create two CTkFrames ============ self.frame_left = customtkinter.CTkFrame(master=self, - width=220, - height=App.HEIGHT-40, - corner_radius=5) + width=220, + height=App.HEIGHT-40, + corner_radius=5) self.frame_left.place(relx=0.38, rely=0.5, anchor=tkinter.E) self.frame_right = customtkinter.CTkFrame(master=self, - width=350, - height=App.HEIGHT-40, - corner_radius=5) + width=350, + height=App.HEIGHT-40, + corner_radius=5) self.frame_right.place(relx=0.40, rely=0.5, anchor=tkinter.W) -# # ============ frame_right ============ - self.button_output = customtkinter.CTkButton(master=self.frame_right, border_color=App.MAIN_COLOR, - fg_color=None, hover_color=App.MAIN_HOVER, - height=28, text="Output Folder", command=self.button_outputFunc, - border_width=3, corner_radius=10, text_font=('Calibri',12)) + fg_color=None, hover_color=App.MAIN_HOVER, + height=28, text="Output Folder", command=self.button_outputFunc, + border_width=3, corner_radius=10, font=('Calibri',12)) self.button_output.place(relx=0.05, rely=0.06, anchor=tkinter.NW) self.entry_output = customtkinter.CTkEntry(master=self.frame_right, width=320, height=38, corner_radius=5) self.entry_output.place(relx=0.05, rely=0.18, anchor=tkinter.NW) def button_outputFunc(self): self.entry_output.delete(0, 'end') - filename = fd.askdirectory() - self.entry_output.insert(0,str(filename)) - pass + filename = customtkinter.filedialog.askdirectory() + self.entry_output.insert(0, str(filename)) def on_closing(self, event=0): self.destroy() diff --git a/test/manual_integration_tests/test_segmented_button.py b/test/manual_integration_tests/test_segmented_button.py index d97e676..58b02a2 100644 --- a/test/manual_integration_tests/test_segmented_button.py +++ b/test/manual_integration_tests/test_segmented_button.py @@ -7,7 +7,8 @@ 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 = customtkinter._CTkSegmentedButton(app, values=[]) +seg_1.configure(values=["value 1", "Value 2", "Value 42", "Value 123", "longlonglong"]) seg_1.pack(padx=20, pady=20) frame_1 = customtkinter.CTkFrame(app, height=100) @@ -16,22 +17,23 @@ 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") +seg_2.configure(values=[]) +seg_2.configure(values=["value 1", "Value 2", "Value 42"]) +seg_2.pack(padx=20, pady=10) +seg_2.insert(0, "insert at 0") +seg_2.insert(1, "insert at 1") label_seg_2 = customtkinter.CTkLabel(frame_1, textvariable=seg_2_var) -label_seg_2.pack(padx=20, pady=20) +label_seg_2.pack(padx=20, pady=10) frame_1_1 = customtkinter.CTkFrame(frame_1, height=100) -frame_1_1.pack(padx=20, pady=20, fill="x") +frame_1_1.pack(padx=20, pady=10, 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_3.pack(padx=20, pady=10) seg_4 = customtkinter._CTkSegmentedButton(app) seg_4.pack(padx=20, pady=20) @@ -41,14 +43,14 @@ seg_5 = customtkinter._CTkSegmentedButton(app, corner_radius=1000, border_width= 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.insert(0, "insert begin") +seg_5.insert(len(seg_5.cget("values")), "insert 1") +seg_5.insert(len(seg_5.cget("values")), "insert 2") +seg_5.insert(len(seg_5.cget("values")), "insert 3") seg_5.configure(fg_color="green") seg_5.set("insert 2") -seg_5.remove_value("insert 2") +seg_5.delete("insert 2") label_seg_5 = customtkinter.CTkLabel(app, textvariable=seg_5_var) label_seg_5.pack(padx=20, pady=20) @@ -58,14 +60,19 @@ 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()), corner_radius=1000) +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 = customtkinter.CTkButton(app, text="insert value", command=lambda: seg_6.insert(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.delete("CTkSegmentedButton") + +seg_7 = customtkinter._CTkSegmentedButton(app, values=["disabled seg button", "2", "3"]) +seg_7.pack(padx=20, pady=20) +seg_7.configure(state="disabled") +seg_7.set("2") app.mainloop() diff --git a/test/manual_integration_tests/test_tabview.py b/test/manual_integration_tests/test_tabview.py index f45f14e..c205b84 100644 --- a/test/manual_integration_tests/test_tabview.py +++ b/test/manual_integration_tests/test_tabview.py @@ -1,16 +1,14 @@ import customtkinter -import tkinter.ttk as ttk app = customtkinter.CTk() -tabview = customtkinter._CTkTabview(app) -tabview.pack(pady=20, padx=20) +tabview_1 = customtkinter._CTkTabview(app) +tabview_1.pack(padx=20, pady=20) +tabview_1.add("tab 1") +tabview_1.insert(0, "tab 0 g |ß$§") -tabview.create_tab(identifier="tab1", text="Tab 1") -tabview.select_tab("tab1") +app.update() - -switch = customtkinter.CTkSwitch(app, text="Darkmode", onvalue="dark", offvalue="light", - command=lambda: customtkinter.set_appearance_mode(switch.get())) -switch.pack(padx=20, pady=20) +tabview_1._segmented_button._buttons_dict["tab 0 g |ß$§"]._text_label.configure(padx=0, pady=0, bd=0) +tabview_1._segmented_button._buttons_dict["tab 0 g |ß$§"]._text_label.configure(bg="red") app.mainloop()