1 Commits

Author SHA1 Message Date
344b30e684 fixed scrollable frame mouse wheel on linux #1356 2023-07-11 14:15:06 +02:00
27 changed files with 153 additions and 313 deletions

View File

@ -10,19 +10,13 @@
![PyPI](https://img.shields.io/pypi/v/customtkinter)
![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)
![PyPI - License](https://img.shields.io/badge/license_MIT)
![PyPI - License](https://img.shields.io/pypi/l/customtkinter)
![](https://tokei.rs/b1/github/tomschimansky/customtkinter)
</div>
---
<div align="center">
<h3>
Official website: https://customtkinter.tomschimansky.com
</h3>
</div>
CustomTkinter is a python UI-library based on Tkinter, which provides new, modern and
fully customizable widgets. They are created and used like normal Tkinter widgets and
can also be used in combination with normal Tkinter elements. The widgets
@ -49,13 +43,14 @@ pip3 install customtkinter
## Documentation
The **official** documentation can be found here:
The **official** documentation can be found in the Wiki Tab here:
**➡️ https://customtkinter.tomschimansky.com/documentation**.
**--> [Documentation](https://github.com/TomSchimansky/CustomTkinter/wiki)**.
## Example Program
To test customtkinter you can try this simple example with only a single button:
```python
import tkinter
import customtkinter
customtkinter.set_appearance_mode("System") # Modes: system (default), light, dark
@ -69,7 +64,7 @@ def button_function():
# Use CTkButton instead of tkinter Button
button = customtkinter.CTkButton(master=app, text="CTkButton", command=button_function)
button.place(relx=0.5, rely=0.5, anchor=customtkinter.CENTER)
button.place(relx=0.5, rely=0.5, anchor=tkinter.CENTER)
app.mainloop()
```

View File

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

View File

@ -34,7 +34,7 @@
"text_color":["gray10", "#DCE4EE"],
"placeholder_text_color": ["gray52", "gray62"]
},
"CTkCheckBox": {
"CTkCheckbox": {
"corner_radius": 6,
"border_width": 3,
"fg_color": ["#3B8ED0", "#1F6AA5"],
@ -48,14 +48,14 @@
"corner_radius": 1000,
"border_width": 3,
"button_length": 0,
"fg_color": ["#939BA2", "#4A4D50"],
"fg_Color": ["#939BA2", "#4A4D50"],
"progress_color": ["#3B8ED0", "#1F6AA5"],
"button_color": ["gray36", "#D5D9DE"],
"button_hover_color": ["gray20", "gray100"],
"text_color": ["gray10", "#DCE4EE"],
"text_color_disabled": ["gray60", "gray45"]
},
"CTkRadioButton": {
"CTkRadiobutton": {
"corner_radius": 1000,
"border_width_checked": 6,
"border_width_unchecked": 3,

View File

@ -34,7 +34,7 @@
"text_color": ["gray14", "gray84"],
"placeholder_text_color": ["gray52", "gray62"]
},
"CTkCheckBox": {
"CTkCheckbox": {
"corner_radius": 6,
"border_width": 3,
"fg_color": ["#3a7ebf", "#1f538d"],
@ -48,14 +48,14 @@
"corner_radius": 1000,
"border_width": 3,
"button_length": 0,
"fg_color": ["#939BA2", "#4A4D50"],
"fg_Color": ["#939BA2", "#4A4D50"],
"progress_color": ["#3a7ebf", "#1f538d"],
"button_color": ["gray36", "#D5D9DE"],
"button_hover_color": ["gray20", "gray100"],
"text_color": ["gray14", "gray84"],
"text_color_disabled": ["gray60", "gray45"]
},
"CTkRadioButton": {
"CTkRadiobutton": {
"corner_radius": 1000,
"border_width_checked": 6,
"border_width_unchecked": 3,
@ -127,9 +127,6 @@
"scrollbar_button_color": ["gray55", "gray41"],
"scrollbar_button_hover_color": ["gray40", "gray53"]
},
"CTkScrollableFrame": {
"label_fg_color": ["gray80", "gray21"]
},
"DropdownMenu": {
"fg_color": ["gray90", "gray20"],
"hover_color": ["gray75", "gray28"],

View File

@ -34,7 +34,7 @@
"text_color":["gray10", "#DCE4EE"],
"placeholder_text_color": ["gray52", "gray62"]
},
"CTkCheckBox": {
"CTkCheckbox": {
"corner_radius": 6,
"border_width": 3,
"fg_color": ["#2CC985", "#2FA572"],
@ -48,14 +48,14 @@
"corner_radius": 1000,
"border_width": 3,
"button_length": 0,
"fg_color": ["#939BA2", "#4A4D50"],
"fg_Color": ["#939BA2", "#4A4D50"],
"progress_color": ["#2CC985", "#2FA572"],
"button_color": ["gray36", "#D5D9DE"],
"button_hover_color": ["gray20", "gray100"],
"text_color": ["gray10", "#DCE4EE"],
"text_color_disabled": ["gray60", "gray45"]
},
"CTkRadioButton": {
"CTkRadiobutton": {
"corner_radius": 1000,
"border_width_checked": 6,
"border_width_unchecked": 3,
@ -127,9 +127,6 @@
"scrollbar_button_color": ["gray55", "gray41"],
"scrollbar_button_hover_color": ["gray40", "gray53"]
},
"CTkScrollableFrame": {
"label_fg_color": ["gray78", "gray23"]
},
"DropdownMenu": {
"fg_color": ["gray90", "gray20"],
"hover_color": ["gray75", "gray28"],

View File

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

View File

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

View File

@ -19,7 +19,7 @@ class CTkToplevel(tkinter.Toplevel, CTkAppearanceModeBaseClass, CTkScalingBaseCl
For detailed information check out the documentation.
"""
_valid_tk_toplevel_arguments: set = {"master", "bd", "borderwidth", "class", "container", "cursor", "height",
_valid_tk_toplevel_arguments: set = {"bd", "borderwidth", "class", "container", "cursor", "height",
"highlightbackground", "highlightthickness", "menu", "relief",
"screen", "takefocus", "use", "visual", "width"}

View File

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

View File

@ -51,20 +51,20 @@ class CTkCheckBox(CTkBaseClass):
self._checkbox_height = checkbox_height
# 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._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._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._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)
# shape
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._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
# text
self._text = text
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_disabled = ThemeManager.theme["CTkCheckBox"]["text_color_disabled"] if text_color_disabled is None else self._check_color_type(text_color_disabled)
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)
# font
self._font = CTkFont() if font is None else self._check_font_type(font)
@ -265,20 +265,12 @@ class CTkCheckBox(CTkBaseClass):
self._hover_color = self._check_color_type(kwargs.pop("hover_color"))
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:
self._text_color = self._check_color_type(kwargs.pop("text_color"))
require_redraw = True
if "text_color_disabled" in kwargs:
self._text_color_disabled = self._check_color_type(kwargs.pop("text_color_disabled"))
if "border_color" in kwargs:
self._border_color = self._check_color_type(kwargs.pop("border_color"))
require_redraw = True
if "hover" in kwargs:

View File

@ -14,8 +14,6 @@ class CTkLabel(CTkBaseClass):
"""
Label with rounded corners. Default is fg_color=None (transparent fg_color).
For detailed information check out the documentation.
state argument will probably be removed because it has no effect
"""
# attributes that are passed to and managed by the tkinter entry only:
@ -31,7 +29,6 @@ class CTkLabel(CTkBaseClass):
bg_color: Union[str, Tuple[str, str]] = "transparent",
fg_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",
font: Optional[Union[tuple, CTkFont]] = None,
@ -48,14 +45,6 @@ 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._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
self._corner_radius = ThemeManager.theme["CTkLabel"]["corner_radius"] if corner_radius is None else corner_radius
@ -170,7 +159,6 @@ class CTkLabel(CTkBaseClass):
outline=self._apply_appearance_mode(self._bg_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))
else:
self._canvas.itemconfig("inner_parts",
@ -178,7 +166,6 @@ class CTkLabel(CTkBaseClass):
outline=self._apply_appearance_mode(self._fg_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))
self._canvas.configure(bg=self._apply_appearance_mode(self._bg_color))
@ -197,10 +184,6 @@ class CTkLabel(CTkBaseClass):
self._text_color = self._check_color_type(kwargs.pop("text_color"))
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:
self._text = kwargs.pop("text")
self._label.configure(text=self._text)
@ -245,8 +228,6 @@ class CTkLabel(CTkBaseClass):
return self._fg_color
elif attribute_name == "text_color":
return self._text_color
elif attribute_name == "text_color_disabled":
return self._text_color_disabled
elif attribute_name == "text":
return self._text

View File

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

View File

@ -50,20 +50,20 @@ class CTkRadioButton(CTkBaseClass):
self._radiobutton_height = radiobutton_height
# 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._border_color = ThemeManager.theme["CTkRadioButton"]["border_color"] if border_color is None else self._check_color_type(border_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._border_color = ThemeManager.theme["CTkRadiobutton"]["border_color"] if border_color is None else self._check_color_type(border_color)
# shape
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_checked = ThemeManager.theme["CTkRadioButton"]["border_width_checked"] if border_width_checked is None else border_width_checked
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_checked = ThemeManager.theme["CTkRadiobutton"]["border_width_checked"] if border_width_checked is None else border_width_checked
# text
self._text = text
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_disabled = ThemeManager.theme["CTkRadioButton"]["text_color_disabled"] if text_color_disabled is None else self._check_color_type(text_color_disabled)
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)
# font
self._font = CTkFont() if font is None else self._check_font_type(font)

View File

@ -74,7 +74,13 @@ class CTkScrollableFrame(tkinter.Frame, CTkAppearanceModeBaseClass, CTkScalingBa
self.bind("<Configure>", lambda e: self._parent_canvas.configure(scrollregion=self._parent_canvas.bbox("all")))
self._parent_canvas.bind("<Configure>", self._fit_frame_dimensions_to_canvas)
self.bind_all("<MouseWheel>", self._mouse_wheel_all, add="+")
if "linux" in sys.platform:
self.bind_all("<Button-4>", self._mouse_wheel_all, add="+")
self.bind_all("<Button-5>", self._mouse_wheel_all, add="+")
else:
self.bind_all("<MouseWheel>", self._mouse_wheel_all, add="+")
self.bind_all("<KeyPress-Shift_L>", self._keyboard_shift_press_all, add="+")
self.bind_all("<KeyPress-Shift_R>", self._keyboard_shift_press_all, add="+")
self.bind_all("<KeyRelease-Shift_L>", self._keyboard_shift_release_all, add="+")
@ -181,10 +187,10 @@ class CTkScrollableFrame(tkinter.Frame, CTkAppearanceModeBaseClass, CTkScalingBa
self._scrollbar.configure(fg_color=kwargs.pop("scrollbar_fg_color"))
if "scrollbar_button_color" in kwargs:
self._scrollbar.configure(button_color=kwargs.pop("scrollbar_button_color"))
self._scrollbar.configure(fg_color=kwargs.pop("scrollbar_button_color"))
if "scrollbar_button_hover_color" in kwargs:
self._scrollbar.configure(button_hover_color=kwargs.pop("scrollbar_button_hover_color"))
self._scrollbar.configure(fg_color=kwargs.pop("scrollbar_button_hover_color"))
if "label_text" in kwargs:
self._label_text = kwargs.pop("label_text")
@ -243,6 +249,8 @@ class CTkScrollableFrame(tkinter.Frame, CTkAppearanceModeBaseClass, CTkScalingBa
self._parent_canvas.configure(xscrollincrement=1, yscrollincrement=1)
elif sys.platform == "darwin":
self._parent_canvas.configure(xscrollincrement=4, yscrollincrement=8)
else:
self._parent_canvas.configure(xscrollincrement=30, yscrollincrement=30)
def _mouse_wheel_all(self, event):
if self.check_if_master_is_canvas(event.widget):
@ -263,10 +271,11 @@ class CTkScrollableFrame(tkinter.Frame, CTkAppearanceModeBaseClass, CTkScalingBa
else:
if self._shift_pressed:
if self._parent_canvas.xview() != (0.0, 1.0):
self._parent_canvas.xview("scroll", -event.delta, "units")
self._parent_canvas.xview_scroll(-1 if event.num == 4 else 1, "units")
else:
if self._parent_canvas.yview() != (0.0, 1.0):
self._parent_canvas.yview("scroll", -event.delta, "units")
self._parent_canvas.yview_scroll(-1 if event.num == 4 else 1, "units")
def _keyboard_shift_press_all(self, event):
self._shift_pressed = True

View File

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

View File

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

View File

@ -54,7 +54,7 @@ class CTkSwitch(CTkBaseClass):
# color
self._border_color = self._check_color_type(border_color, transparency=True)
self._fg_color = ThemeManager.theme["CTkSwitch"]["fg_color"] if fg_color is None else self._check_color_type(fg_color)
self._fg_color = ThemeManager.theme["CTkSwitch"]["fg_Color"] if fg_color is None else self._check_color_type(fg_color)
self._progress_color = ThemeManager.theme["CTkSwitch"]["progress_color"] if progress_color is None else self._check_color_type(progress_color, transparency=True)
self._button_color = ThemeManager.theme["CTkSwitch"]["button_color"] if button_color is None else self._check_color_type(button_color)
self._button_hover_color = ThemeManager.theme["CTkSwitch"]["button_hover_color"] if button_hover_color is None else self._check_color_type(button_hover_color)
@ -299,10 +299,6 @@ class CTkSwitch(CTkBaseClass):
self._fg_color = self._check_color_type(kwargs.pop("fg_color"))
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:
self._progress_color = self._check_color_type(kwargs.pop("progress_color"), transparency=True)
require_redraw = True
@ -315,12 +311,8 @@ class CTkSwitch(CTkBaseClass):
self._button_hover_color = self._check_color_type(kwargs.pop("button_hover_color"))
require_redraw = True
if "text_color" in kwargs:
self._text_color = self._check_color_type(kwargs.pop("text_color"))
require_redraw = True
if "text_color_disabled" in kwargs:
self._text_color_disabled = self._check_color_type(kwargs.pop("text_color_disabled"))
if "border_color" in kwargs:
self._border_color = self._check_color_type(kwargs.pop("border_color"), transparency=True)
require_redraw = True
if "hover" in kwargs:

View File

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

View File

@ -52,10 +52,7 @@ class CTkFont(Font):
def remove_size_configure_callback(self, callback: Callable):
""" remove function, that gets called when font got configured """
try:
self._size_configure_callback_list.remove(callback)
except ValueError:
pass
self._size_configure_callback_list.remove(callback)
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)"""

View File

@ -4,6 +4,6 @@ from .theme_manager import ThemeManager
try:
ThemeManager.load_theme("blue")
except FileNotFoundError as err:
raise FileNotFoundError(f"{err}\nThe .json theme file for CustomTkinter could not be found.\n" +
raise FileNotFoundError(f"{err}\n\nThe .json theme file for CustomTkinter could not be found.\n" +
f"If packaging with pyinstaller was used, have a look at the wiki:\n" +
f"https://github.com/TomSchimansky/CustomTkinter/wiki/Packaging#windows-pyinstaller-auto-py-to-exe")

View File

@ -1,6 +1,5 @@
import sys
import os
import pathlib
import json
from typing import List, Union
@ -16,8 +15,7 @@ class ThemeManager:
script_directory = os.path.dirname(os.path.abspath(__file__))
if theme_name_or_path in cls._built_in_themes:
customtkinter_path = pathlib.Path(script_directory).parent.parent.parent
with open(os.path.join(customtkinter_path, "assets", "themes", f"{theme_name_or_path}.json"), "r") as f:
with open(os.path.join(script_directory, "../../../assets", "themes", f"{theme_name_or_path}.json"), "r") as f:
cls.theme = json.load(f)
else:
with open(theme_name_or_path, "r") as f:
@ -37,12 +35,6 @@ class ThemeManager:
else:
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
def save_theme(cls):
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.pack(pady=10, padx=10)
tabview_1 = customtkinter.CTkTabview(master=frame_1, width=300)
tabview_1 = customtkinter.CTkTabview(master=frame_1, width=200, height=70)
tabview_1.pack(pady=10, padx=10)
tabview_1.add("CTkTabview")
tabview_1.add("Tab 2")

View File

@ -3,10 +3,11 @@ requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
[tool.tbump]
# Uncomment this if your project is hosted on GitHub:
github_url = "https://github.com/TomSchimansky/CustomTkinter"
[tool.tbump.version]
current = "5.2.0"
current = "5.1.1"
# Example of a semver regexp.
# Make sure this matches current_version before
@ -32,3 +33,17 @@ src = "setup.cfg"
[[tool.tbump.file]]
src = "customtkinter/__init__.py"
search = "__version__ = \"{current_version}\""
# You can specify a list of commands to
# run after the files have been patched
# and before the git commit is made
# [[tool.tbump.before_commit]]
# name = "check changelog"
# cmd = "grep -q {new_version} Changelog.rst"
# Or run some commands after the git tag and the branch
# have been pushed:
# [[tool.tbump.after_push]]
# name = "publish"
# cmd = "./publish.sh"

View File

@ -1,23 +1,18 @@
[metadata]
name = customtkinter
version = 5.2.0
version = 5.1.1
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://github.com/TomSchimansky/CustomTkinter
long_description_content_type = text/markdown
url = https://customtkinter.tomschimansky.com
url = https://github.com/TomSchimansky/CustomTkinter
author = Tom Schimansky
license = Creative Commons Zero v1.0 Universal
license_file = LICENSE
classifiers =
License :: OSI Approved :: MIT License
License :: CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
Operating System :: OS Independent
Programming Language :: Python :: 3 :: Only
[project.urls]
homepage = https://customtkinter.tomschimansky.com
documentation = https://customtkinter.tomschimansky.com/documentation
repository = https://github.com/tomschimansky/customtkinter
[options]
python_requires = >=3.7
packages =

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -1,7 +1,5 @@
import customtkinter
customtkinter.set_default_color_theme("dark-blue")
app = customtkinter.CTk()
app.grid_columnconfigure(2, weight=1)
@ -28,7 +26,7 @@ frame_4.configure(label_text="CTkScrollableFrame")
frame_5 = customtkinter.CTkScrollableFrame(app, orientation="vertical", label_text="CTkScrollableFrame", corner_radius=0)
frame_5.grid(row=0, column=2, rowspan=2, sticky="nsew")
for i in range(100):
for i in range(20):
customtkinter.CTkCheckBox(frame_1).grid(row=i, padx=10, pady=10)
customtkinter.CTkCheckBox(frame_2).grid(row=i, padx=10, pady=10)
customtkinter.CTkCheckBox(frame_3).grid(row=0, column=i, padx=10, pady=10)