20 Commits

Author SHA1 Message Date
d719950f80 fixed tabview initial fg_color, removed tests from simple_example.py 2023-07-27 14:40:19 +02:00
16990a566f added anchor to tabview #1766, fixed tabview fg_color update #1803, added index method to tabview and segmented button, added rename method to tabview #1192, fixed license in Readme.md 2023-07-27 14:06:13 +02:00
f629e4c5cb fix simple example 2023-07-08 21:32:20 +02:00
9cfd5f4515 added missing arguments to CTkSLider configure #1790 2023-07-08 21:24:59 +02:00
e116faf910 add font option to input dialog #838 2023-06-20 13:38:47 +02:00
b8f1eea411 fix license classifier 2023-06-19 14:01:01 +02:00
8b7386bc64 fix license classifier 2023-06-19 13:57:11 +02:00
8fc2d31584 Bump to 5.2.0 2023-06-19 13:54:51 +02:00
290cafbc39 Merge pull request #1399 from Sahil481/customtkinter-sahil-contribute
Fix configuring anchor on CTkButton #1394 #1393
2023-06-19 13:19:50 +02:00
9bd6d7bdde Merge pull request #1729 from dishb/fix-cancel-event
Fix incorrect event in input dialog
2023-06-19 13:00:37 +02:00
6841f94a99 🛠️ [fix] fix issue in #1631 2023-06-16 12:54:16 -07:00
a0c0da6c9a Merge pull request #1721 from dishb/fix-readme-1
README has unnecessary tkinter import
2023-06-16 13:17:47 +02:00
10d5cd4c2a 🛠️ [fix] small mistake in the README example code 2023-06-15 16:07:18 -07:00
abe33f7e81 fix error in CTkFont with multiple destroy calls #1692 2023-06-15 12:49:07 +02:00
37b375d58b fixed wrong json class names and added fix for backwards compatibility in ThemeManager #1538 2023-05-27 13:25:30 +02:00
cd85a1c782 added text_color_disabled to CTkLabel #1557, change license in setup.cfg 2023-05-27 12:48:46 +02:00
1960c0b1e0 added text_color_disabled to configure of CTkOptionMenu #1559 2023-05-08 19:11:22 +02:00
6d2f31c23c added border_width, corner_radius to configure of CTkSegmentedButton, removed border_color because has no effect #1562 2023-05-08 19:06:39 +02:00
c6b16ce815 added checkmark_color and text_color_disabled to CTkCheckbox configure method #1586 2023-05-08 13:08:44 +02:00
9f653fab1d Fixed a bug
Fixed #1394
2023-03-29 22:14:43 +05:30
22 changed files with 263 additions and 110 deletions

View File

@ -10,7 +10,7 @@
![PyPI](https://img.shields.io/pypi/v/customtkinter) ![PyPI](https://img.shields.io/pypi/v/customtkinter)
![PyPI - Downloads](https://img.shields.io/pypi/dm/customtkinter?color=green&label=downloads) ![PyPI - Downloads](https://img.shields.io/pypi/dm/customtkinter?color=green&label=downloads)
![Downloads](https://static.pepy.tech/personalized-badge/customtkinter?period=total&units=international_system&left_color=grey&right_color=green&left_text=downloads) ![Downloads](https://static.pepy.tech/personalized-badge/customtkinter?period=total&units=international_system&left_color=grey&right_color=green&left_text=downloads)
![PyPI - License](https://img.shields.io/pypi/l/customtkinter) ![PyPI - License](https://img.shields.io/badge/license_MIT)
![](https://tokei.rs/b1/github/tomschimansky/customtkinter) ![](https://tokei.rs/b1/github/tomschimansky/customtkinter)
</div> </div>
@ -56,7 +56,6 @@ The **official** documentation can be found here:
## Example Program ## Example Program
To test customtkinter you can try this simple example with only a single button: To test customtkinter you can try this simple example with only a single button:
```python ```python
import tkinter
import customtkinter import customtkinter
customtkinter.set_appearance_mode("System") # Modes: system (default), light, dark customtkinter.set_appearance_mode("System") # Modes: system (default), light, dark
@ -70,7 +69,7 @@ def button_function():
# Use CTkButton instead of tkinter Button # Use CTkButton instead of tkinter Button
button = customtkinter.CTkButton(master=app, text="CTkButton", command=button_function) button = customtkinter.CTkButton(master=app, text="CTkButton", command=button_function)
button.place(relx=0.5, rely=0.5, anchor=tkinter.CENTER) button.place(relx=0.5, rely=0.5, anchor=customtkinter.CENTER)
app.mainloop() app.mainloop()
``` ```

View File

@ -1,4 +1,4 @@
__version__ = "5.1.3" __version__ = "5.2.0"
import os import os
import sys import sys

View File

@ -34,7 +34,7 @@
"text_color":["gray10", "#DCE4EE"], "text_color":["gray10", "#DCE4EE"],
"placeholder_text_color": ["gray52", "gray62"] "placeholder_text_color": ["gray52", "gray62"]
}, },
"CTkCheckbox": { "CTkCheckBox": {
"corner_radius": 6, "corner_radius": 6,
"border_width": 3, "border_width": 3,
"fg_color": ["#3B8ED0", "#1F6AA5"], "fg_color": ["#3B8ED0", "#1F6AA5"],
@ -55,7 +55,7 @@
"text_color": ["gray10", "#DCE4EE"], "text_color": ["gray10", "#DCE4EE"],
"text_color_disabled": ["gray60", "gray45"] "text_color_disabled": ["gray60", "gray45"]
}, },
"CTkRadiobutton": { "CTkRadioButton": {
"corner_radius": 1000, "corner_radius": 1000,
"border_width_checked": 6, "border_width_checked": 6,
"border_width_unchecked": 3, "border_width_unchecked": 3,

View File

@ -34,7 +34,7 @@
"text_color": ["gray14", "gray84"], "text_color": ["gray14", "gray84"],
"placeholder_text_color": ["gray52", "gray62"] "placeholder_text_color": ["gray52", "gray62"]
}, },
"CTkCheckbox": { "CTkCheckBox": {
"corner_radius": 6, "corner_radius": 6,
"border_width": 3, "border_width": 3,
"fg_color": ["#3a7ebf", "#1f538d"], "fg_color": ["#3a7ebf", "#1f538d"],
@ -55,7 +55,7 @@
"text_color": ["gray14", "gray84"], "text_color": ["gray14", "gray84"],
"text_color_disabled": ["gray60", "gray45"] "text_color_disabled": ["gray60", "gray45"]
}, },
"CTkRadiobutton": { "CTkRadioButton": {
"corner_radius": 1000, "corner_radius": 1000,
"border_width_checked": 6, "border_width_checked": 6,
"border_width_unchecked": 3, "border_width_unchecked": 3,

View File

@ -34,7 +34,7 @@
"text_color":["gray10", "#DCE4EE"], "text_color":["gray10", "#DCE4EE"],
"placeholder_text_color": ["gray52", "gray62"] "placeholder_text_color": ["gray52", "gray62"]
}, },
"CTkCheckbox": { "CTkCheckBox": {
"corner_radius": 6, "corner_radius": 6,
"border_width": 3, "border_width": 3,
"fg_color": ["#2CC985", "#2FA572"], "fg_color": ["#2CC985", "#2FA572"],
@ -55,7 +55,7 @@
"text_color": ["gray10", "#DCE4EE"], "text_color": ["gray10", "#DCE4EE"],
"text_color_disabled": ["gray60", "gray45"] "text_color_disabled": ["gray60", "gray45"]
}, },
"CTkRadiobutton": { "CTkRadioButton": {
"corner_radius": 1000, "corner_radius": 1000,
"border_width_checked": 6, "border_width_checked": 6,
"border_width_unchecked": 3, "border_width_unchecked": 3,

View File

@ -5,6 +5,7 @@ from .widgets import CTkEntry
from .widgets import CTkButton from .widgets import CTkButton
from .widgets.theme import ThemeManager from .widgets.theme import ThemeManager
from .ctk_toplevel import CTkToplevel from .ctk_toplevel import CTkToplevel
from .widgets.font import CTkFont
class CTkInputDialog(CTkToplevel): class CTkInputDialog(CTkToplevel):
@ -24,6 +25,7 @@ class CTkInputDialog(CTkToplevel):
entry_text_color: Optional[Union[str, Tuple[str, str]]] = None, entry_text_color: Optional[Union[str, Tuple[str, str]]] = None,
title: str = "CTkDialog", title: str = "CTkDialog",
font: Optional[Union[tuple, CTkFont]] = None,
text: str = "CTkDialog"): text: str = "CTkDialog"):
super().__init__(fg_color=fg_color) super().__init__(fg_color=fg_color)
@ -39,9 +41,11 @@ class CTkInputDialog(CTkToplevel):
self._user_input: Union[str, None] = None self._user_input: Union[str, None] = None
self._running: bool = False self._running: bool = False
self._title = title
self._text = text self._text = text
self._font = font
self.title(title) self.title(self._title)
self.lift() # lift window on top self.lift() # lift window on top
self.attributes("-topmost", True) # stay on top self.attributes("-topmost", True) # stay on top
self.protocol("WM_DELETE_WINDOW", self._on_closing) self.protocol("WM_DELETE_WINDOW", self._on_closing)
@ -50,7 +54,6 @@ class CTkInputDialog(CTkToplevel):
self.grab_set() # make other windows not clickable self.grab_set() # make other windows not clickable
def _create_widgets(self): def _create_widgets(self):
self.grid_columnconfigure((0, 1), weight=1) self.grid_columnconfigure((0, 1), weight=1)
self.rowconfigure(0, weight=1) self.rowconfigure(0, weight=1)
@ -59,14 +62,16 @@ class CTkInputDialog(CTkToplevel):
wraplength=300, wraplength=300,
fg_color="transparent", fg_color="transparent",
text_color=self._text_color, text_color=self._text_color,
text=self._text,) text=self._text,
font=self._font)
self._label.grid(row=0, column=0, columnspan=2, padx=20, pady=20, sticky="ew") self._label.grid(row=0, column=0, columnspan=2, padx=20, pady=20, sticky="ew")
self._entry = CTkEntry(master=self, self._entry = CTkEntry(master=self,
width=230, width=230,
fg_color=self._entry_fg_color, fg_color=self._entry_fg_color,
border_color=self._entry_border_color, border_color=self._entry_border_color,
text_color=self._entry_text_color) text_color=self._entry_text_color,
font=self._font)
self._entry.grid(row=1, column=0, columnspan=2, padx=20, pady=(0, 20), sticky="ew") self._entry.grid(row=1, column=0, columnspan=2, padx=20, pady=(0, 20), sticky="ew")
self._ok_button = CTkButton(master=self, self._ok_button = CTkButton(master=self,
@ -76,6 +81,7 @@ class CTkInputDialog(CTkToplevel):
hover_color=self._button_hover_color, hover_color=self._button_hover_color,
text_color=self._button_text_color, text_color=self._button_text_color,
text='Ok', text='Ok',
font=self._font,
command=self._ok_event) command=self._ok_event)
self._ok_button.grid(row=2, column=0, columnspan=1, padx=(20, 10), pady=(0, 20), sticky="ew") self._ok_button.grid(row=2, column=0, columnspan=1, padx=(20, 10), pady=(0, 20), sticky="ew")
@ -86,7 +92,8 @@ class CTkInputDialog(CTkToplevel):
hover_color=self._button_hover_color, hover_color=self._button_hover_color,
text_color=self._button_text_color, text_color=self._button_text_color,
text='Cancel', text='Cancel',
command=self._ok_event) font=self._font,
command=self._cancel_event)
self._cancel_button.grid(row=2, column=1, columnspan=1, padx=(10, 20), pady=(0, 20), sticky="ew") self._cancel_button.grid(row=2, column=1, columnspan=1, padx=(10, 20), pady=(0, 20), sticky="ew")
self.after(150, lambda: self._entry.focus()) # set focus to entry with slight delay, otherwise it won't work self.after(150, lambda: self._entry.focus()) # set focus to entry with slight delay, otherwise it won't work

View File

@ -1,4 +1,5 @@
import tkinter import tkinter
import tkinterDnD
from distutils.version import StrictVersion as Version from distutils.version import StrictVersion as Version
import sys import sys
import os import os
@ -12,8 +13,10 @@ from .widgets.appearance_mode import CTkAppearanceModeBaseClass
from customtkinter.windows.widgets.utility.utility_functions import pop_from_dict_by_set, check_kwargs_empty from customtkinter.windows.widgets.utility.utility_functions import pop_from_dict_by_set, check_kwargs_empty
TK_CLASS = tkinterDnD.Tk
class CTk(tkinter.Tk, CTkAppearanceModeBaseClass, CTkScalingBaseClass):
class CTk(TK_CLASS, CTkAppearanceModeBaseClass, CTkScalingBaseClass):
""" """
Main app window with dark titlebar on Windows and macOS. Main app window with dark titlebar on Windows and macOS.
For detailed information check out the documentation. For detailed information check out the documentation.
@ -35,7 +38,7 @@ class CTk(tkinter.Tk, CTkAppearanceModeBaseClass, CTkScalingBaseClass):
self._enable_macos_dark_title_bar() self._enable_macos_dark_title_bar()
# call init methods of super classes # call init methods of super classes
tkinter.Tk.__init__(self, **pop_from_dict_by_set(kwargs, self._valid_tk_constructor_arguments)) TK_CLASS.__init__(self, **pop_from_dict_by_set(kwargs, self._valid_tk_constructor_arguments))
CTkAppearanceModeBaseClass.__init__(self) CTkAppearanceModeBaseClass.__init__(self)
CTkScalingBaseClass.__init__(self, scaling_type="window") CTkScalingBaseClass.__init__(self, scaling_type="window")
check_kwargs_empty(kwargs, raise_error=True) check_kwargs_empty(kwargs, raise_error=True)

View File

@ -436,6 +436,7 @@ class CTkButton(CTkBaseClass):
if "anchor" in kwargs: if "anchor" in kwargs:
self._anchor = kwargs.pop("anchor") self._anchor = kwargs.pop("anchor")
self._create_grid()
require_redraw = True require_redraw = True
super().configure(require_redraw=require_redraw, **kwargs) super().configure(require_redraw=require_redraw, **kwargs)

View File

@ -51,20 +51,20 @@ class CTkCheckBox(CTkBaseClass):
self._checkbox_height = checkbox_height self._checkbox_height = checkbox_height
# color # color
self._fg_color = ThemeManager.theme["CTkCheckbox"]["fg_color"] if fg_color is None else self._check_color_type(fg_color) self._fg_color = ThemeManager.theme["CTkCheckBox"]["fg_color"] if fg_color is None else self._check_color_type(fg_color)
self._hover_color = ThemeManager.theme["CTkCheckbox"]["hover_color"] if hover_color is None else self._check_color_type(hover_color) self._hover_color = ThemeManager.theme["CTkCheckBox"]["hover_color"] if hover_color is None else self._check_color_type(hover_color)
self._border_color = ThemeManager.theme["CTkCheckbox"]["border_color"] if border_color is None else self._check_color_type(border_color) self._border_color = ThemeManager.theme["CTkCheckBox"]["border_color"] if border_color is None else self._check_color_type(border_color)
self._checkmark_color = ThemeManager.theme["CTkCheckbox"]["checkmark_color"] if checkmark_color is None else self._check_color_type(checkmark_color) self._checkmark_color = ThemeManager.theme["CTkCheckBox"]["checkmark_color"] if checkmark_color is None else self._check_color_type(checkmark_color)
# shape # shape
self._corner_radius = ThemeManager.theme["CTkCheckbox"]["corner_radius"] if corner_radius is None else corner_radius self._corner_radius = ThemeManager.theme["CTkCheckBox"]["corner_radius"] if corner_radius is None else corner_radius
self._border_width = ThemeManager.theme["CTkCheckbox"]["border_width"] if border_width is None else border_width self._border_width = ThemeManager.theme["CTkCheckBox"]["border_width"] if border_width is None else border_width
# text # text
self._text = text self._text = text
self._text_label: Union[tkinter.Label, None] = None self._text_label: Union[tkinter.Label, None] = None
self._text_color = ThemeManager.theme["CTkCheckbox"]["text_color"] if text_color is None else self._check_color_type(text_color) self._text_color = ThemeManager.theme["CTkCheckBox"]["text_color"] if text_color is None else self._check_color_type(text_color)
self._text_color_disabled = ThemeManager.theme["CTkCheckbox"]["text_color_disabled"] if text_color_disabled is None else self._check_color_type(text_color_disabled) self._text_color_disabled = ThemeManager.theme["CTkCheckBox"]["text_color_disabled"] if text_color_disabled is None else self._check_color_type(text_color_disabled)
# font # font
self._font = CTkFont() if font is None else self._check_font_type(font) self._font = CTkFont() if font is None else self._check_font_type(font)
@ -265,12 +265,20 @@ class CTkCheckBox(CTkBaseClass):
self._hover_color = self._check_color_type(kwargs.pop("hover_color")) self._hover_color = self._check_color_type(kwargs.pop("hover_color"))
require_redraw = True require_redraw = True
if "border_color" in kwargs:
self._border_color = self._check_color_type(kwargs.pop("border_color"))
require_redraw = True
if "checkmark_color" in kwargs:
self._checkmark_color = self._check_color_type(kwargs.pop("checkmark_color"))
require_redraw = True
if "text_color" in kwargs: if "text_color" in kwargs:
self._text_color = self._check_color_type(kwargs.pop("text_color")) self._text_color = self._check_color_type(kwargs.pop("text_color"))
require_redraw = True require_redraw = True
if "border_color" in kwargs: if "text_color_disabled" in kwargs:
self._border_color = self._check_color_type(kwargs.pop("border_color")) self._text_color_disabled = self._check_color_type(kwargs.pop("text_color_disabled"))
require_redraw = True require_redraw = True
if "hover" in kwargs: if "hover" in kwargs:

View File

@ -31,6 +31,7 @@ class CTkLabel(CTkBaseClass):
bg_color: Union[str, Tuple[str, str]] = "transparent", bg_color: Union[str, Tuple[str, str]] = "transparent",
fg_color: Optional[Union[str, Tuple[str, str]]] = None, fg_color: Optional[Union[str, Tuple[str, str]]] = None,
text_color: Optional[Union[str, Tuple[str, str]]] = None, text_color: Optional[Union[str, Tuple[str, str]]] = None,
text_color_disabled: Optional[Union[str, Tuple[str, str]]] = None,
text: str = "CTkLabel", text: str = "CTkLabel",
font: Optional[Union[tuple, CTkFont]] = None, font: Optional[Union[tuple, CTkFont]] = None,
@ -47,6 +48,14 @@ class CTkLabel(CTkBaseClass):
self._fg_color = ThemeManager.theme["CTkLabel"]["fg_color"] if fg_color is None else self._check_color_type(fg_color, transparency=True) self._fg_color = ThemeManager.theme["CTkLabel"]["fg_color"] if fg_color is None else self._check_color_type(fg_color, transparency=True)
self._text_color = ThemeManager.theme["CTkLabel"]["text_color"] if text_color is None else self._check_color_type(text_color) self._text_color = ThemeManager.theme["CTkLabel"]["text_color"] if text_color is None else self._check_color_type(text_color)
if text_color_disabled is None:
if "text_color_disabled" in ThemeManager.theme["CTkLabel"]:
self._text_color_disabled = ThemeManager.theme["CTkLabel"]["text_color"]
else:
self._text_color_disabled = self._text_color
else:
self._text_color_disabled = self._check_color_type(text_color_disabled)
# shape # shape
self._corner_radius = ThemeManager.theme["CTkLabel"]["corner_radius"] if corner_radius is None else corner_radius self._corner_radius = ThemeManager.theme["CTkLabel"]["corner_radius"] if corner_radius is None else corner_radius
@ -161,6 +170,7 @@ class CTkLabel(CTkBaseClass):
outline=self._apply_appearance_mode(self._bg_color)) outline=self._apply_appearance_mode(self._bg_color))
self._label.configure(fg=self._apply_appearance_mode(self._text_color), self._label.configure(fg=self._apply_appearance_mode(self._text_color),
disabledforeground=self._apply_appearance_mode(self._text_color_disabled),
bg=self._apply_appearance_mode(self._bg_color)) bg=self._apply_appearance_mode(self._bg_color))
else: else:
self._canvas.itemconfig("inner_parts", self._canvas.itemconfig("inner_parts",
@ -168,6 +178,7 @@ class CTkLabel(CTkBaseClass):
outline=self._apply_appearance_mode(self._fg_color)) outline=self._apply_appearance_mode(self._fg_color))
self._label.configure(fg=self._apply_appearance_mode(self._text_color), self._label.configure(fg=self._apply_appearance_mode(self._text_color),
disabledforeground=self._apply_appearance_mode(self._text_color_disabled),
bg=self._apply_appearance_mode(self._fg_color)) bg=self._apply_appearance_mode(self._fg_color))
self._canvas.configure(bg=self._apply_appearance_mode(self._bg_color)) self._canvas.configure(bg=self._apply_appearance_mode(self._bg_color))
@ -186,6 +197,10 @@ class CTkLabel(CTkBaseClass):
self._text_color = self._check_color_type(kwargs.pop("text_color")) self._text_color = self._check_color_type(kwargs.pop("text_color"))
require_redraw = True require_redraw = True
if "text_color_disabled" in kwargs:
self._text_color_disabled = self._check_color_type(kwargs.pop("text_color_disabled"))
require_redraw = True
if "text" in kwargs: if "text" in kwargs:
self._text = kwargs.pop("text") self._text = kwargs.pop("text")
self._label.configure(text=self._text) self._label.configure(text=self._text)
@ -230,6 +245,8 @@ class CTkLabel(CTkBaseClass):
return self._fg_color return self._fg_color
elif attribute_name == "text_color": elif attribute_name == "text_color":
return self._text_color return self._text_color
elif attribute_name == "text_color_disabled":
return self._text_color_disabled
elif attribute_name == "text": elif attribute_name == "text":
return self._text return self._text

View File

@ -243,6 +243,10 @@ class CTkOptionMenu(CTkBaseClass):
self._text_color = self._check_color_type(kwargs.pop("text_color")) self._text_color = self._check_color_type(kwargs.pop("text_color"))
require_redraw = True require_redraw = True
if "text_color_disabled" in kwargs:
self._text_color_disabled = self._check_color_type(kwargs.pop("text_color_disabled"))
require_redraw = True
if "dropdown_fg_color" in kwargs: if "dropdown_fg_color" in kwargs:
self._dropdown_menu.configure(fg_color=kwargs.pop("dropdown_fg_color")) self._dropdown_menu.configure(fg_color=kwargs.pop("dropdown_fg_color"))
@ -261,8 +265,12 @@ class CTkOptionMenu(CTkBaseClass):
self._update_font() self._update_font()
if "command" in kwargs: if "dropdown_font" in kwargs:
self._command = kwargs.pop("command") self._dropdown_menu.configure(font=kwargs.pop("dropdown_font"))
if "values" in kwargs:
self._values = kwargs.pop("values")
self._dropdown_menu.configure(values=self._values)
if "variable" in kwargs: if "variable" in kwargs:
if self._variable is not None: # remove old callback if self._variable is not None: # remove old callback
@ -277,19 +285,15 @@ class CTkOptionMenu(CTkBaseClass):
else: else:
self._variable = None self._variable = None
if "values" in kwargs: if "state" in kwargs:
self._values = kwargs.pop("values") self._state = kwargs.pop("state")
self._dropdown_menu.configure(values=self._values) require_redraw = True
if "dropdown_font" in kwargs:
self._dropdown_menu.configure(font=kwargs.pop("dropdown_font"))
if "hover" in kwargs: if "hover" in kwargs:
self._hover = kwargs.pop("hover") self._hover = kwargs.pop("hover")
if "state" in kwargs: if "command" in kwargs:
self._state = kwargs.pop("state") self._command = kwargs.pop("command")
require_redraw = True
if "dynamic_resizing" in kwargs: if "dynamic_resizing" in kwargs:
self._dynamic_resizing = kwargs.pop("dynamic_resizing") self._dynamic_resizing = kwargs.pop("dynamic_resizing")

View File

@ -50,20 +50,20 @@ class CTkRadioButton(CTkBaseClass):
self._radiobutton_height = radiobutton_height self._radiobutton_height = radiobutton_height
# color # color
self._fg_color = ThemeManager.theme["CTkRadiobutton"]["fg_color"] if fg_color is None else self._check_color_type(fg_color) self._fg_color = ThemeManager.theme["CTkRadioButton"]["fg_color"] if fg_color is None else self._check_color_type(fg_color)
self._hover_color = ThemeManager.theme["CTkRadiobutton"]["hover_color"] if hover_color is None else self._check_color_type(hover_color) self._hover_color = ThemeManager.theme["CTkRadioButton"]["hover_color"] if hover_color is None else self._check_color_type(hover_color)
self._border_color = ThemeManager.theme["CTkRadiobutton"]["border_color"] if border_color is None else self._check_color_type(border_color) self._border_color = ThemeManager.theme["CTkRadioButton"]["border_color"] if border_color is None else self._check_color_type(border_color)
# shape # shape
self._corner_radius = ThemeManager.theme["CTkRadiobutton"]["corner_radius"] if corner_radius is None else corner_radius self._corner_radius = ThemeManager.theme["CTkRadioButton"]["corner_radius"] if corner_radius is None else corner_radius
self._border_width_unchecked = ThemeManager.theme["CTkRadiobutton"]["border_width_unchecked"] if border_width_unchecked is None else border_width_unchecked self._border_width_unchecked = ThemeManager.theme["CTkRadioButton"]["border_width_unchecked"] if border_width_unchecked is None else border_width_unchecked
self._border_width_checked = ThemeManager.theme["CTkRadiobutton"]["border_width_checked"] if border_width_checked is None else border_width_checked self._border_width_checked = ThemeManager.theme["CTkRadioButton"]["border_width_checked"] if border_width_checked is None else border_width_checked
# text # text
self._text = text self._text = text
self._text_label: Union[tkinter.Label, None] = None self._text_label: Union[tkinter.Label, None] = None
self._text_color = ThemeManager.theme["CTkRadiobutton"]["text_color"] if text_color is None else self._check_color_type(text_color) self._text_color = ThemeManager.theme["CTkRadioButton"]["text_color"] if text_color is None else self._check_color_type(text_color)
self._text_color_disabled = ThemeManager.theme["CTkRadiobutton"]["text_color_disabled"] if text_color_disabled is None else self._check_color_type(text_color_disabled) self._text_color_disabled = ThemeManager.theme["CTkRadioButton"]["text_color_disabled"] if text_color_disabled is None else self._check_color_type(text_color_disabled)
# font # font
self._font = CTkFont() if font is None else self._check_font_type(font) self._font = CTkFont() if font is None else self._check_font_type(font)

View File

@ -10,6 +10,7 @@ from .theme import ThemeManager
from .font import CTkFont from .font import CTkFont
from .ctk_button import CTkButton from .ctk_button import CTkButton
from .ctk_frame import CTkFrame from .ctk_frame import CTkFrame
from .utility import check_kwargs_empty
class CTkSegmentedButton(CTkFrame): class CTkSegmentedButton(CTkFrame):
@ -40,10 +41,9 @@ class CTkSegmentedButton(CTkFrame):
variable: Union[tkinter.Variable, None] = None, variable: Union[tkinter.Variable, None] = None,
dynamic_resizing: bool = True, dynamic_resizing: bool = True,
command: Union[Callable[[str], None], None] = None, command: Union[Callable[[str], None], None] = None,
state: str = "normal", state: str = "normal"):
**kwargs):
super().__init__(master=master, bg_color=bg_color, width=width, height=height, **kwargs) super().__init__(master=master, bg_color=bg_color, width=width, height=height)
self._sb_fg_color = ThemeManager.theme["CTkSegmentedButton"]["fg_color"] if fg_color is None else self._check_color_type(fg_color) self._sb_fg_color = ThemeManager.theme["CTkSegmentedButton"]["fg_color"] if fg_color is None else self._check_color_type(fg_color)
@ -186,7 +186,7 @@ class CTkSegmentedButton(CTkFrame):
for index, value in enumerate(self._value_list): for index, value in enumerate(self._value_list):
self.grid_columnconfigure(index, weight=1, minsize=self._current_height) self.grid_columnconfigure(index, weight=1, minsize=self._current_height)
self._buttons_dict[value].grid(row=0, column=index, sticky="ew") self._buttons_dict[value].grid(row=0, column=index, sticky="nsew")
def _create_buttons_from_values(self): def _create_buttons_from_values(self):
assert len(self._buttons_dict) == 0 assert len(self._buttons_dict) == 0
@ -197,6 +197,23 @@ class CTkSegmentedButton(CTkFrame):
self._configure_button_corners_for_index(index) self._configure_button_corners_for_index(index)
def configure(self, **kwargs): def configure(self, **kwargs):
if "width" in kwargs:
super().configure(width=kwargs.pop("width"))
if "height" in kwargs:
super().configure(height=kwargs.pop("height"))
if "corner_radius" in kwargs:
self._sb_corner_radius = kwargs.pop("corner_radius")
super().configure(corner_radius=self._sb_corner_radius)
for button in self._buttons_dict.values():
button.configure(corner_radius=self._sb_corner_radius)
if "border_width" in kwargs:
self._sb_border_width = kwargs.pop("border_width")
for button in self._buttons_dict.values():
button.configure(border_width=self._sb_border_width)
if "bg_color" in kwargs: if "bg_color" in kwargs:
super().configure(bg_color=kwargs.pop("bg_color")) super().configure(bg_color=kwargs.pop("bg_color"))
@ -296,14 +313,20 @@ class CTkSegmentedButton(CTkFrame):
for button in self._buttons_dict.values(): for button in self._buttons_dict.values():
button.configure(state=self._state) button.configure(state=self._state)
super().configure(**kwargs) check_kwargs_empty(kwargs, raise_error=True)
def cget(self, attribute_name: str) -> any: def cget(self, attribute_name: str) -> any:
if attribute_name == "corner_radius": if attribute_name == "width":
return super().cget(attribute_name)
elif attribute_name == "height":
return super().cget(attribute_name)
elif attribute_name == "corner_radius":
return self._sb_corner_radius return self._sb_corner_radius
elif attribute_name == "border_width": elif attribute_name == "border_width":
return self._sb_border_width return self._sb_border_width
elif attribute_name == "bg_color":
return super().cget(attribute_name)
elif attribute_name == "fg_color": elif attribute_name == "fg_color":
return self._sb_fg_color return self._sb_fg_color
elif attribute_name == "selected_color": elif attribute_name == "selected_color":
@ -331,7 +354,7 @@ class CTkSegmentedButton(CTkFrame):
return self._command return self._command
else: else:
return super().cget(attribute_name) raise ValueError(f"'{attribute_name}' is not a supported argument. Look at the documentation for supported arguments.")
def set(self, value: str, from_variable_callback: bool = False, from_button_callback: bool = False): def set(self, value: str, from_variable_callback: bool = False, from_button_callback: bool = False):
if value == self._current_value: if value == self._current_value:
@ -360,6 +383,9 @@ class CTkSegmentedButton(CTkFrame):
def get(self) -> str: def get(self) -> str:
return self._current_value return self._current_value
def index(self, value: str) -> int:
return self._value_list.index(value)
def insert(self, index: int, value: str): def insert(self, index: int, value: str):
if value not in self._buttons_dict: if value not in self._buttons_dict:
if value != "": if value != "":

View File

@ -199,15 +199,30 @@ class CTkSlider(CTkBaseClass):
outline=self._apply_appearance_mode(self._button_color)) outline=self._apply_appearance_mode(self._button_color))
def configure(self, require_redraw=False, **kwargs): def configure(self, require_redraw=False, **kwargs):
if "state" in kwargs: if "corner_radius" in kwargs:
self._state = kwargs.pop("state") self._corner_radius = kwargs.pop("corner_radius")
self._set_cursor() require_redraw = True
if "button_corner_radius" in kwargs:
self._button_corner_radius = kwargs.pop("button_corner_radius")
require_redraw = True
if "border_width" in kwargs:
self._border_width = kwargs.pop("border_width")
require_redraw = True
if "button_length" in kwargs:
self._button_length = kwargs.pop("button_length")
require_redraw = True require_redraw = True
if "fg_color" in kwargs: if "fg_color" in kwargs:
self._fg_color = self._check_color_type(kwargs.pop("fg_color")) self._fg_color = self._check_color_type(kwargs.pop("fg_color"))
require_redraw = True require_redraw = True
if "border_color" in kwargs:
self._border_color = self._check_color_type(kwargs.pop("border_color"), transparency=True)
require_redraw = True
if "progress_color" in kwargs: if "progress_color" in kwargs:
self._progress_color = self._check_color_type(kwargs.pop("progress_color"), transparency=True) self._progress_color = self._check_color_type(kwargs.pop("progress_color"), transparency=True)
require_redraw = True require_redraw = True
@ -220,20 +235,17 @@ class CTkSlider(CTkBaseClass):
self._button_hover_color = self._check_color_type(kwargs.pop("button_hover_color")) self._button_hover_color = self._check_color_type(kwargs.pop("button_hover_color"))
require_redraw = True require_redraw = True
if "border_color" in kwargs:
self._border_color = self._check_color_type(kwargs.pop("border_color"), transparency=True)
require_redraw = True
if "border_width" in kwargs:
self._border_width = kwargs.pop("border_width")
require_redraw = True
if "from_" in kwargs: if "from_" in kwargs:
self._from_ = kwargs.pop("from_") self._from_ = kwargs.pop("from_")
if "to" in kwargs: if "to" in kwargs:
self._to = kwargs.pop("to") self._to = kwargs.pop("to")
if "state" in kwargs:
self._state = kwargs.pop("state")
self._set_cursor()
require_redraw = True
if "number_of_steps" in kwargs: if "number_of_steps" in kwargs:
self._number_of_steps = kwargs.pop("number_of_steps") self._number_of_steps = kwargs.pop("number_of_steps")
@ -255,6 +267,10 @@ class CTkSlider(CTkBaseClass):
else: else:
self._variable = None self._variable = None
if "orientation" in kwargs:
self._orientation = kwargs.pop("orientation")
require_redraw = True
super().configure(require_redraw=require_redraw, **kwargs) super().configure(require_redraw=require_redraw, **kwargs)
def cget(self, attribute_name: str) -> any: def cget(self, attribute_name: str) -> any:

View File

@ -15,8 +15,8 @@ class CTkTabview(CTkBaseClass):
For detailed information check out the documentation. For detailed information check out the documentation.
""" """
_top_spacing: int = 10 # px on top of the buttons _outer_spacing: int = 10 # px on top or below the button
_top_button_overhang: int = 8 # px _outer_button_overhang: int = 8 # px
_button_height: int = 26 _button_height: int = 26
_segmented_button_border_width: int = 3 _segmented_button_border_width: int = 3
@ -41,6 +41,7 @@ class CTkTabview(CTkBaseClass):
text_color_disabled: Optional[Union[str, Tuple[str, str]]] = None, text_color_disabled: Optional[Union[str, Tuple[str, str]]] = None,
command: Union[Callable, None] = None, command: Union[Callable, None] = None,
anchor: str = "center",
state: str = "normal", state: str = "normal",
**kwargs): **kwargs):
@ -65,12 +66,13 @@ class CTkTabview(CTkBaseClass):
# shape # shape
self._corner_radius = ThemeManager.theme["CTkFrame"]["corner_radius"] if corner_radius is None else corner_radius self._corner_radius = ThemeManager.theme["CTkFrame"]["corner_radius"] if corner_radius is None else corner_radius
self._border_width = ThemeManager.theme["CTkFrame"]["border_width"] if border_width is None else border_width self._border_width = ThemeManager.theme["CTkFrame"]["border_width"] if border_width is None else border_width
self._anchor = anchor
self._canvas = CTkCanvas(master=self, self._canvas = CTkCanvas(master=self,
bg=self._apply_appearance_mode(self._bg_color), bg=self._apply_appearance_mode(self._bg_color),
highlightthickness=0, highlightthickness=0,
width=self._apply_widget_scaling(self._desired_width), width=self._apply_widget_scaling(self._desired_width),
height=self._apply_widget_scaling(self._desired_height - self._top_spacing - self._top_button_overhang)) height=self._apply_widget_scaling(self._desired_height - self._outer_spacing - self._outer_button_overhang))
self._draw_engine = DrawEngine(self._canvas) self._draw_engine = DrawEngine(self._canvas)
self._segmented_button = CTkSegmentedButton(self, self._segmented_button = CTkSegmentedButton(self,
@ -99,9 +101,9 @@ class CTkTabview(CTkBaseClass):
self._draw() self._draw()
def _segmented_button_callback(self, selected_name): def _segmented_button_callback(self, selected_name):
self._set_grid_tab_by_name(selected_name)
self._tab_dict[self._current_name].grid_forget() self._tab_dict[self._current_name].grid_forget()
self._current_name = selected_name self._current_name = selected_name
self._set_grid_current_tab()
if self._command is not None: if self._command is not None:
self._command() self._command()
@ -124,7 +126,7 @@ class CTkTabview(CTkBaseClass):
super()._set_scaling(*args, **kwargs) super()._set_scaling(*args, **kwargs)
self._canvas.configure(width=self._apply_widget_scaling(self._desired_width), 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)) height=self._apply_widget_scaling(self._desired_height - self._outer_spacing - self._outer_button_overhang))
self._configure_grid() self._configure_grid()
self._draw(no_color_updates=True) self._draw(no_color_updates=True)
@ -132,56 +134,82 @@ class CTkTabview(CTkBaseClass):
super()._set_dimensions(width, height) super()._set_dimensions(width, height)
self._canvas.configure(width=self._apply_widget_scaling(self._desired_width), 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)) height=self._apply_widget_scaling(self._desired_height - self._outer_spacing - self._outer_button_overhang))
self._draw() self._draw()
def _configure_segmented_button_background_corners(self): def _configure_segmented_button_background_corners(self):
""" needs to be called for changes in fg_color, bg_color """ """ needs to be called for changes in fg_color, bg_color """
if self._fg_color is not None: if self._fg_color == "transparent":
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)) self._segmented_button.configure(background_corner_colors=(self._bg_color, self._bg_color, self._bg_color, self._bg_color))
else:
def _configure_tab_background_corners_by_name(self, name: str): if self._anchor.lower() in ("center", "w", "nw", "n", "ne", "e", "e"):
""" needs to be called for changes in fg_color, bg_color, border_width """ self._segmented_button.configure(background_corner_colors=(self._bg_color, self._bg_color, self._fg_color, self._fg_color))
else:
self._tab_dict[name].configure(background_corner_colors=None) self._segmented_button.configure(background_corner_colors=(self._fg_color, self._fg_color, self._bg_color, self._bg_color))
def _configure_grid(self): def _configure_grid(self):
""" create 3 x 4 grid system """ """ create 3 x 4 grid system """
self.grid_rowconfigure(0, weight=0, minsize=self._apply_widget_scaling(self._top_spacing)) if self._anchor.lower() in ("center", "w", "nw", "n", "ne", "e", "e"):
self.grid_rowconfigure(1, weight=0, minsize=self._apply_widget_scaling(self._top_button_overhang)) self.grid_rowconfigure(0, weight=0, minsize=self._apply_widget_scaling(self._outer_spacing))
self.grid_rowconfigure(2, weight=0, minsize=self._apply_widget_scaling(self._button_height - self._top_button_overhang)) self.grid_rowconfigure(1, weight=0, minsize=self._apply_widget_scaling(self._outer_button_overhang))
self.grid_rowconfigure(3, weight=1) self.grid_rowconfigure(2, weight=0, minsize=self._apply_widget_scaling(self._button_height - self._outer_button_overhang))
self.grid_rowconfigure(3, weight=1)
else:
self.grid_rowconfigure(0, weight=1)
self.grid_rowconfigure(1, weight=0, minsize=self._apply_widget_scaling(self._button_height - self._outer_button_overhang))
self.grid_rowconfigure(2, weight=0, minsize=self._apply_widget_scaling(self._outer_button_overhang))
self.grid_rowconfigure(3, weight=0, minsize=self._apply_widget_scaling(self._outer_spacing))
self.grid_columnconfigure(0, weight=1) self.grid_columnconfigure(0, weight=1)
def _set_grid_canvas(self): def _set_grid_canvas(self):
self._canvas.grid(row=2, rowspan=2, column=0, columnspan=1, sticky="nsew") if self._anchor.lower() in ("center", "w", "nw", "n", "ne", "e", "e"):
self._canvas.grid(row=2, rowspan=2, column=0, columnspan=1, sticky="nsew")
else:
self._canvas.grid(row=0, rowspan=2, column=0, columnspan=1, sticky="nsew")
def _set_grid_segmented_button(self): def _set_grid_segmented_button(self):
""" needs to be called for changes in corner_radius """ """ needs to be called for changes in corner_radius, anchor """
self._segmented_button.grid(row=1, rowspan=2, column=0, columnspan=1, padx=self._apply_widget_scaling(self._corner_radius), sticky="ns")
def _set_grid_tab_by_name(self, name: str): if self._anchor.lower() in ("center", "n", "s"):
self._segmented_button.grid(row=1, rowspan=2, column=0, columnspan=1, padx=self._apply_widget_scaling(self._corner_radius), sticky="ns")
elif self._anchor.lower() in ("nw", "w", "sw"):
self._segmented_button.grid(row=1, rowspan=2, column=0, columnspan=1, padx=self._apply_widget_scaling(self._corner_radius), sticky="nsw")
elif self._anchor.lower() in ("ne", "e", "se"):
self._segmented_button.grid(row=1, rowspan=2, column=0, columnspan=1, padx=self._apply_widget_scaling(self._corner_radius), sticky="nse")
def _set_grid_current_tab(self):
""" needs to be called for changes in corner_radius, border_width """ """ needs to be called for changes in corner_radius, border_width """
self._tab_dict[name].grid(row=3, column=0, sticky="nsew", if self._anchor.lower() in ("center", "w", "nw", "n", "ne", "e", "e"):
padx=self._apply_widget_scaling(max(self._corner_radius, self._border_width)), self._tab_dict[self._current_name].grid(row=3, column=0, sticky="nsew",
pady=self._apply_widget_scaling(max(self._corner_radius, self._border_width))) padx=self._apply_widget_scaling(max(self._corner_radius, self._border_width)),
pady=self._apply_widget_scaling(max(self._corner_radius, self._border_width)))
else:
self._tab_dict[self._current_name].grid(row=0, 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 _grid_forget_all_tabs(self): def _grid_forget_all_tabs(self, exclude_name=None):
for frame in self._tab_dict.values(): for name, frame in self._tab_dict.items():
frame.grid_forget() if name != exclude_name:
frame.grid_forget()
def _create_tab(self) -> CTkFrame: def _create_tab(self) -> CTkFrame:
new_tab = CTkFrame(self, new_tab = CTkFrame(self,
height=0, height=0,
width=0, width=0,
fg_color=self._fg_color,
border_width=0, border_width=0,
corner_radius=self._corner_radius) corner_radius=0)
if self._fg_color == "transparent":
new_tab.configure(fg_color=self._apply_appearance_mode(self._bg_color),
bg_color=self._apply_appearance_mode(self._bg_color))
else:
new_tab.configure(fg_color=self._apply_appearance_mode(self._fg_color),
bg_color=self._apply_appearance_mode(self._fg_color))
return new_tab return new_tab
def _draw(self, no_color_updates: bool = False): def _draw(self, no_color_updates: bool = False):
@ -191,7 +219,7 @@ class CTkTabview(CTkBaseClass):
return return
requires_recoloring = self._draw_engine.draw_rounded_rect_with_border(self._apply_widget_scaling(self._current_width), requires_recoloring = self._draw_engine.draw_rounded_rect_with_border(self._apply_widget_scaling(self._current_width),
self._apply_widget_scaling(self._current_height - self._top_spacing - self._top_button_overhang), self._apply_widget_scaling(self._current_height - self._outer_spacing - self._outer_button_overhang),
self._apply_widget_scaling(self._corner_radius), self._apply_widget_scaling(self._corner_radius),
self._apply_widget_scaling(self._border_width)) self._apply_widget_scaling(self._border_width))
@ -200,27 +228,37 @@ class CTkTabview(CTkBaseClass):
self._canvas.itemconfig("inner_parts", self._canvas.itemconfig("inner_parts",
fill=self._apply_appearance_mode(self._bg_color), fill=self._apply_appearance_mode(self._bg_color),
outline=self._apply_appearance_mode(self._bg_color)) outline=self._apply_appearance_mode(self._bg_color))
for tab in self._tab_dict.values():
tab.configure(fg_color=self._apply_appearance_mode(self._bg_color),
bg_color=self._apply_appearance_mode(self._bg_color))
else: else:
self._canvas.itemconfig("inner_parts", self._canvas.itemconfig("inner_parts",
fill=self._apply_appearance_mode(self._fg_color), fill=self._apply_appearance_mode(self._fg_color),
outline=self._apply_appearance_mode(self._fg_color)) outline=self._apply_appearance_mode(self._fg_color))
for tab in self._tab_dict.values():
tab.configure(fg_color=self._apply_appearance_mode(self._fg_color),
bg_color=self._apply_appearance_mode(self._fg_color))
self._canvas.itemconfig("border_parts", self._canvas.itemconfig("border_parts",
fill=self._apply_appearance_mode(self._border_color), fill=self._apply_appearance_mode(self._border_color),
outline=self._apply_appearance_mode(self._border_color)) outline=self._apply_appearance_mode(self._border_color))
self._canvas.configure(bg=self._apply_appearance_mode(self._bg_color)) self._canvas.configure(bg=self._apply_appearance_mode(self._bg_color))
tkinter.Frame.configure(self, bg=self._apply_appearance_mode(self._bg_color)) # configure bg color of tkinter.Frame, cuase canvas does not fill frame tkinter.Frame.configure(self, bg=self._apply_appearance_mode(self._bg_color)) # configure bg color of tkinter.Frame, cause canvas does not fill frame
def configure(self, require_redraw=False, **kwargs): def configure(self, require_redraw=False, **kwargs):
if "corner_radius" in kwargs: if "corner_radius" in kwargs:
self._corner_radius = kwargs.pop("corner_radius") self._corner_radius = kwargs.pop("corner_radius")
require_redraw = True self._set_grid_segmented_button()
self._set_grid_current_tab()
self._set_grid_canvas()
self._configure_segmented_button_background_corners()
self._segmented_button.configure(corner_radius=self._corner_radius)
if "border_width" in kwargs: if "border_width" in kwargs:
self._border_width = kwargs.pop("border_width") self._border_width = kwargs.pop("border_width")
require_redraw = True require_redraw = True
if "fg_color" in kwargs: if "fg_color" in kwargs:
self._fg_color = self._check_color_type(kwargs.pop("fg_color"), transparency=True) self._fg_color = self._check_color_type(kwargs.pop("fg_color"), transparency=True)
self._configure_segmented_button_background_corners()
require_redraw = True require_redraw = True
if "border_color" in kwargs: if "border_color" in kwargs:
self._border_color = self._check_color_type(kwargs.pop("border_color")) self._border_color = self._check_color_type(kwargs.pop("border_color"))
@ -242,6 +280,10 @@ class CTkTabview(CTkBaseClass):
if "command" in kwargs: if "command" in kwargs:
self._command = kwargs.pop("command") self._command = kwargs.pop("command")
if "anchor" in kwargs:
self._anchor = kwargs.pop("anchor")
self._configure_grid()
self._set_grid_segmented_button()
if "state" in kwargs: if "state" in kwargs:
self._segmented_button.configure(state=kwargs.pop("state")) self._segmented_button.configure(state=kwargs.pop("state"))
@ -274,6 +316,8 @@ class CTkTabview(CTkBaseClass):
elif attribute_name == "command": elif attribute_name == "command":
return self._command return self._command
elif attribute_name == "anchor":
return self._anchor
elif attribute_name == "state": elif attribute_name == "state":
return self._segmented_button.cget(attribute_name) return self._segmented_button.cget(attribute_name)
@ -296,17 +340,16 @@ class CTkTabview(CTkBaseClass):
if len(self._tab_dict) == 0: if len(self._tab_dict) == 0:
self._set_grid_segmented_button() self._set_grid_segmented_button()
self._name_list.insert(index, name) self._name_list.append(name)
self._tab_dict[name] = self._create_tab() self._tab_dict[name] = self._create_tab()
self._segmented_button.insert(index, name) self._segmented_button.insert(index, name)
self._configure_tab_background_corners_by_name(name)
# if created tab is only tab select this tab # if created tab is only tab select this tab
if len(self._tab_dict) == 1: if len(self._tab_dict) == 1:
self._current_name = name self._current_name = name
self._segmented_button.set(self._current_name) self._segmented_button.set(self._current_name)
self._grid_forget_all_tabs() self._grid_forget_all_tabs()
self._set_grid_tab_by_name(self._current_name) self._set_grid_current_tab()
return self._tab_dict[name] return self._tab_dict[name]
else: else:
@ -316,6 +359,10 @@ class CTkTabview(CTkBaseClass):
""" appends new tab with given name """ """ appends new tab with given name """
return self.insert(len(self._tab_dict), name) return self.insert(len(self._tab_dict), name)
def index(self, name) -> int:
""" get index of tab with given name """
return self._segmented_button.index(name)
def move(self, new_index: int, name: str): def move(self, new_index: int, name: str):
if 0 <= new_index < len(self._name_list): if 0 <= new_index < len(self._name_list):
if name in self._tab_dict: if name in self._tab_dict:
@ -325,6 +372,22 @@ class CTkTabview(CTkBaseClass):
else: else:
raise ValueError(f"CTkTabview new_index {new_index} not in range of name list with len {len(self._name_list)}") raise ValueError(f"CTkTabview new_index {new_index} not in range of name list with len {len(self._name_list)}")
def rename(self, old_name: str, new_name: str):
if new_name in self._name_list:
raise ValueError(f"new_name '{new_name}' already exists")
# segmented button
old_index = self._segmented_button.index(old_name)
self._segmented_button.delete(old_name)
self._segmented_button.insert(old_index, new_name)
# name list
self._name_list.remove(old_name)
self._name_list.append(new_name)
# tab dictionary
self._tab_dict[new_name] = self._tab_dict.pop(old_name)
def delete(self, name: str): def delete(self, name: str):
""" delete tab by name """ """ delete tab by name """
@ -344,7 +407,7 @@ class CTkTabview(CTkBaseClass):
self._current_name = self._name_list[0] self._current_name = self._name_list[0]
self._segmented_button.set(self._current_name) self._segmented_button.set(self._current_name)
self._grid_forget_all_tabs() self._grid_forget_all_tabs()
self._set_grid_tab_by_name(self._current_name) self._set_grid_current_tab()
# more tabs are left # more tabs are left
else: else:
@ -360,8 +423,8 @@ class CTkTabview(CTkBaseClass):
if name in self._tab_dict: if name in self._tab_dict:
self._current_name = name self._current_name = name
self._segmented_button.set(name) self._segmented_button.set(name)
self._grid_forget_all_tabs() self._set_grid_current_tab()
self._set_grid_tab_by_name(name) self.after(100, lambda: self._grid_forget_all_tabs(exclude_name=name))
else: else:
raise ValueError(f"CTkTabview has no tab named '{name}'") raise ValueError(f"CTkTabview has no tab named '{name}'")

View File

@ -52,7 +52,10 @@ class CTkFont(Font):
def remove_size_configure_callback(self, callback: Callable): def remove_size_configure_callback(self, callback: Callable):
""" remove function, that gets called when font got configured """ """ remove function, that gets called when font got configured """
self._size_configure_callback_list.remove(callback) try:
self._size_configure_callback_list.remove(callback)
except ValueError:
pass
def create_scaled_tuple(self, font_scaling: float) -> Tuple[str, int, str]: def create_scaled_tuple(self, font_scaling: float) -> Tuple[str, int, str]:
""" return scaled tuple representation of font in the form (family: str, size: int, style: str)""" """ return scaled tuple representation of font in the form (family: str, size: int, style: str)"""

View File

@ -37,6 +37,12 @@ class ThemeManager:
else: else:
cls.theme[key] = cls.theme[key]["Linux"] cls.theme[key] = cls.theme[key]["Linux"]
# fix name inconsistencies
if "CTkCheckbox" in cls.theme.keys():
cls.theme["CTkCheckBox"] = cls.theme.pop("CTkCheckbox")
if "CTkRadiobutton" in cls.theme.keys():
cls.theme["CTkRadioButton"] = cls.theme.pop("CTkRadiobutton")
@classmethod @classmethod
def save_theme(cls): def save_theme(cls):
if cls._currently_loaded_theme is not None: if cls._currently_loaded_theme is not None:

View File

@ -63,7 +63,7 @@ text_1.insert("0.0", "CTkTextbox\n\n\n\n")
segmented_button_1 = customtkinter.CTkSegmentedButton(master=frame_1, values=["CTkSegmentedButton", "Value 2"]) segmented_button_1 = customtkinter.CTkSegmentedButton(master=frame_1, values=["CTkSegmentedButton", "Value 2"])
segmented_button_1.pack(pady=10, padx=10) segmented_button_1.pack(pady=10, padx=10)
tabview_1 = customtkinter.CTkTabview(master=frame_1, width=200, height=70) tabview_1 = customtkinter.CTkTabview(master=frame_1, width=300)
tabview_1.pack(pady=10, padx=10) tabview_1.pack(pady=10, padx=10)
tabview_1.add("CTkTabview") tabview_1.add("CTkTabview")
tabview_1.add("Tab 2") tabview_1.add("Tab 2")

View File

@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
github_url = "https://github.com/TomSchimansky/CustomTkinter" github_url = "https://github.com/TomSchimansky/CustomTkinter"
[tool.tbump.version] [tool.tbump.version]
current = "5.1.3" current = "5.2.0"
# Example of a semver regexp. # Example of a semver regexp.
# Make sure this matches current_version before # Make sure this matches current_version before

View File

@ -1,6 +1,6 @@
[project] [metadata]
name = customtkinter name = customtkinter
version = 5.1.3 version = 5.2.0
description = Create modern looking GUIs with Python description = Create modern looking GUIs with Python
long_description = A modern and customizable python UI-library based on Tkinter: https://customtkinter.tomschimansky.com long_description = A modern and customizable python UI-library based on Tkinter: https://customtkinter.tomschimansky.com
long_description_content_type = text/markdown long_description_content_type = text/markdown
@ -9,7 +9,7 @@ author = Tom Schimansky
license = Creative Commons Zero v1.0 Universal license = Creative Commons Zero v1.0 Universal
license_file = LICENSE license_file = LICENSE
classifiers = classifiers =
License :: CC0 1.0 Universal (CC0 1.0) Public Domain Dedication License :: OSI Approved :: MIT License
Operating System :: OS Independent Operating System :: OS Independent
Programming Language :: Python :: 3 :: Only Programming Language :: Python :: 3 :: Only

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB