1 Commits

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

View File

@ -17,12 +17,6 @@
--- ---
<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 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 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 can also be used in combination with normal Tkinter elements. The widgets
@ -49,13 +43,14 @@ pip3 install customtkinter
## Documentation ## 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 ## 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
@ -69,7 +64,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=customtkinter.CENTER) button.place(relx=0.5, rely=0.5, anchor=tkinter.CENTER)
app.mainloop() app.mainloop()
``` ```

View File

@ -1,4 +1,4 @@
__version__ = "5.2.0" __version__ = "5.1.1"
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"],
@ -48,14 +48,14 @@
"corner_radius": 1000, "corner_radius": 1000,
"border_width": 3, "border_width": 3,
"button_length": 0, "button_length": 0,
"fg_color": ["#939BA2", "#4A4D50"], "fg_Color": ["#939BA2", "#4A4D50"],
"progress_color": ["#3B8ED0", "#1F6AA5"], "progress_color": ["#3B8ED0", "#1F6AA5"],
"button_color": ["gray36", "#D5D9DE"], "button_color": ["gray36", "#D5D9DE"],
"button_hover_color": ["gray20", "gray100"], "button_hover_color": ["gray20", "gray100"],
"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"],
@ -48,14 +48,14 @@
"corner_radius": 1000, "corner_radius": 1000,
"border_width": 3, "border_width": 3,
"button_length": 0, "button_length": 0,
"fg_color": ["#939BA2", "#4A4D50"], "fg_Color": ["#939BA2", "#4A4D50"],
"progress_color": ["#3a7ebf", "#1f538d"], "progress_color": ["#3a7ebf", "#1f538d"],
"button_color": ["gray36", "#D5D9DE"], "button_color": ["gray36", "#D5D9DE"],
"button_hover_color": ["gray20", "gray100"], "button_hover_color": ["gray20", "gray100"],
"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,
@ -127,9 +127,6 @@
"scrollbar_button_color": ["gray55", "gray41"], "scrollbar_button_color": ["gray55", "gray41"],
"scrollbar_button_hover_color": ["gray40", "gray53"] "scrollbar_button_hover_color": ["gray40", "gray53"]
}, },
"CTkScrollableFrame": {
"label_fg_color": ["gray80", "gray21"]
},
"DropdownMenu": { "DropdownMenu": {
"fg_color": ["gray90", "gray20"], "fg_color": ["gray90", "gray20"],
"hover_color": ["gray75", "gray28"], "hover_color": ["gray75", "gray28"],

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"],
@ -48,14 +48,14 @@
"corner_radius": 1000, "corner_radius": 1000,
"border_width": 3, "border_width": 3,
"button_length": 0, "button_length": 0,
"fg_color": ["#939BA2", "#4A4D50"], "fg_Color": ["#939BA2", "#4A4D50"],
"progress_color": ["#2CC985", "#2FA572"], "progress_color": ["#2CC985", "#2FA572"],
"button_color": ["gray36", "#D5D9DE"], "button_color": ["gray36", "#D5D9DE"],
"button_hover_color": ["gray20", "gray100"], "button_hover_color": ["gray20", "gray100"],
"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,
@ -127,9 +127,6 @@
"scrollbar_button_color": ["gray55", "gray41"], "scrollbar_button_color": ["gray55", "gray41"],
"scrollbar_button_hover_color": ["gray40", "gray53"] "scrollbar_button_hover_color": ["gray40", "gray53"]
}, },
"CTkScrollableFrame": {
"label_fg_color": ["gray78", "gray23"]
},
"DropdownMenu": { "DropdownMenu": {
"fg_color": ["gray90", "gray20"], "fg_color": ["gray90", "gray20"],
"hover_color": ["gray75", "gray28"], "hover_color": ["gray75", "gray28"],

View File

@ -86,7 +86,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='Cancel', text='Cancel',
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._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

@ -19,7 +19,7 @@ class CTkToplevel(tkinter.Toplevel, CTkAppearanceModeBaseClass, CTkScalingBaseCl
For detailed information check out the documentation. 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", "highlightbackground", "highlightthickness", "menu", "relief",
"screen", "takefocus", "use", "visual", "width"} "screen", "takefocus", "use", "visual", "width"}

View File

@ -436,7 +436,6 @@ 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,20 +265,12 @@ 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 "text_color_disabled" in kwargs: if "border_color" in kwargs:
self._text_color_disabled = self._check_color_type(kwargs.pop("text_color_disabled")) self._border_color = self._check_color_type(kwargs.pop("border_color"))
require_redraw = True require_redraw = True
if "hover" in kwargs: 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). Label with rounded corners. Default is fg_color=None (transparent fg_color).
For detailed information check out the documentation. 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: # 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", 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,
@ -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._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
@ -170,7 +159,6 @@ 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",
@ -178,7 +166,6 @@ 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))
@ -197,10 +184,6 @@ 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)
@ -245,8 +228,6 @@ 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,10 +243,6 @@ 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"))
@ -265,12 +261,8 @@ class CTkOptionMenu(CTkBaseClass):
self._update_font() self._update_font()
if "dropdown_font" in kwargs: if "command" in kwargs:
self._dropdown_menu.configure(font=kwargs.pop("dropdown_font")) self._command = kwargs.pop("command")
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
@ -285,15 +277,19 @@ class CTkOptionMenu(CTkBaseClass):
else: else:
self._variable = None self._variable = None
if "state" in kwargs: if "values" in kwargs:
self._state = kwargs.pop("state") self._values = kwargs.pop("values")
require_redraw = True 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: if "hover" in kwargs:
self._hover = kwargs.pop("hover") self._hover = kwargs.pop("hover")
if "command" in kwargs: if "state" in kwargs:
self._command = kwargs.pop("command") self._state = kwargs.pop("state")
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

@ -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.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._parent_canvas.bind("<Configure>", self._fit_frame_dimensions_to_canvas)
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("<MouseWheel>", self._mouse_wheel_all, add="+")
self.bind_all("<KeyPress-Shift_L>", self._keyboard_shift_press_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("<KeyPress-Shift_R>", self._keyboard_shift_press_all, add="+")
self.bind_all("<KeyRelease-Shift_L>", self._keyboard_shift_release_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")) self._scrollbar.configure(fg_color=kwargs.pop("scrollbar_fg_color"))
if "scrollbar_button_color" in kwargs: 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: 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: if "label_text" in kwargs:
self._label_text = kwargs.pop("label_text") 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) self._parent_canvas.configure(xscrollincrement=1, yscrollincrement=1)
elif sys.platform == "darwin": elif sys.platform == "darwin":
self._parent_canvas.configure(xscrollincrement=4, yscrollincrement=8) self._parent_canvas.configure(xscrollincrement=4, yscrollincrement=8)
else:
self._parent_canvas.configure(xscrollincrement=30, yscrollincrement=30)
def _mouse_wheel_all(self, event): def _mouse_wheel_all(self, event):
if self.check_if_master_is_canvas(event.widget): if self.check_if_master_is_canvas(event.widget):
@ -263,10 +271,11 @@ class CTkScrollableFrame(tkinter.Frame, CTkAppearanceModeBaseClass, CTkScalingBa
else: else:
if self._shift_pressed: if self._shift_pressed:
if self._parent_canvas.xview() != (0.0, 1.0): 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: else:
if self._parent_canvas.yview() != (0.0, 1.0): 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): def _keyboard_shift_press_all(self, event):
self._shift_pressed = True self._shift_pressed = True

View File

@ -10,7 +10,6 @@ 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):
@ -41,9 +40,10 @@ 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) 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) 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="nsew") self._buttons_dict[value].grid(row=0, column=index, sticky="ew")
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,23 +197,6 @@ 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"))
@ -313,20 +296,14 @@ 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)
check_kwargs_empty(kwargs, raise_error=True) super().configure(**kwargs)
def cget(self, attribute_name: str) -> any: def cget(self, attribute_name: str) -> any:
if attribute_name == "width": if attribute_name == "corner_radius":
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":
@ -354,7 +331,7 @@ class CTkSegmentedButton(CTkFrame):
return self._command return self._command
else: 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): 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:

View File

@ -54,7 +54,7 @@ class CTkSwitch(CTkBaseClass):
# color # color
self._border_color = self._check_color_type(border_color, transparency=True) 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._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_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) 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")) 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
@ -315,12 +311,8 @@ class CTkSwitch(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 "text_color" in kwargs: if "border_color" in kwargs:
self._text_color = self._check_color_type(kwargs.pop("text_color")) self._border_color = self._check_color_type(kwargs.pop("border_color"), transparency=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 require_redraw = True
if "hover" in kwargs: if "hover" in kwargs:

View File

@ -99,9 +99,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._current_name = selected_name self._current_name = selected_name
self._grid_forget_all_tabs()
self._set_grid_tab_by_name(self._current_name)
if self._command is not None: if self._command is not None:
self._command() self._command()
@ -171,9 +171,8 @@ class CTkTabview(CTkBaseClass):
padx=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))) pady=self._apply_widget_scaling(max(self._corner_radius, self._border_width)))
def _grid_forget_all_tabs(self, exclude_name=None): def _grid_forget_all_tabs(self):
for name, frame in self._tab_dict.items(): for frame in self._tab_dict.values():
if name != exclude_name:
frame.grid_forget() frame.grid_forget()
def _create_tab(self) -> CTkFrame: def _create_tab(self) -> CTkFrame:
@ -361,8 +360,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_tab_by_name(name) 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,10 +52,7 @@ 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 """
try:
self._size_configure_callback_list.remove(callback) 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

@ -4,6 +4,6 @@ from .theme_manager import ThemeManager
try: try:
ThemeManager.load_theme("blue") ThemeManager.load_theme("blue")
except FileNotFoundError as err: 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"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") f"https://github.com/TomSchimansky/CustomTkinter/wiki/Packaging#windows-pyinstaller-auto-py-to-exe")

View File

@ -1,6 +1,5 @@
import sys import sys
import os import os
import pathlib
import json import json
from typing import List, Union from typing import List, Union
@ -16,8 +15,7 @@ class ThemeManager:
script_directory = os.path.dirname(os.path.abspath(__file__)) script_directory = os.path.dirname(os.path.abspath(__file__))
if theme_name_or_path in cls._built_in_themes: if theme_name_or_path in cls._built_in_themes:
customtkinter_path = pathlib.Path(script_directory).parent.parent.parent with open(os.path.join(script_directory, "../../../assets", "themes", f"{theme_name_or_path}.json"), "r") as f:
with open(os.path.join(customtkinter_path, "assets", "themes", f"{theme_name_or_path}.json"), "r") as f:
cls.theme = json.load(f) cls.theme = json.load(f)
else: else:
with open(theme_name_or_path, "r") as f: with open(theme_name_or_path, "r") as f:
@ -37,12 +35,6 @@ 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

@ -3,10 +3,11 @@ requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta" build-backend = "setuptools.build_meta"
[tool.tbump] [tool.tbump]
# Uncomment this if your project is hosted on GitHub:
github_url = "https://github.com/TomSchimansky/CustomTkinter" github_url = "https://github.com/TomSchimansky/CustomTkinter"
[tool.tbump.version] [tool.tbump.version]
current = "5.2.0" current = "5.1.1"
# Example of a semver regexp. # Example of a semver regexp.
# Make sure this matches current_version before # Make sure this matches current_version before
@ -32,3 +33,17 @@ src = "setup.cfg"
[[tool.tbump.file]] [[tool.tbump.file]]
src = "customtkinter/__init__.py" src = "customtkinter/__init__.py"
search = "__version__ = \"{current_version}\"" 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] [metadata]
name = customtkinter name = customtkinter
version = 5.2.0 version = 5.1.1
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://github.com/TomSchimansky/CustomTkinter
long_description_content_type = text/markdown long_description_content_type = text/markdown
url = https://customtkinter.tomschimansky.com url = https://github.com/TomSchimansky/CustomTkinter
author = Tom Schimansky 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 :: MIT License License :: CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
Operating System :: OS Independent Operating System :: OS Independent
Programming Language :: Python :: 3 :: Only 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] [options]
python_requires = >=3.7 python_requires = >=3.7
packages = packages =

View File

@ -1,7 +1,5 @@
import customtkinter import customtkinter
customtkinter.set_default_color_theme("dark-blue")
app = customtkinter.CTk() app = customtkinter.CTk()
app.grid_columnconfigure(2, weight=1) 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 = customtkinter.CTkScrollableFrame(app, orientation="vertical", label_text="CTkScrollableFrame", corner_radius=0)
frame_5.grid(row=0, column=2, rowspan=2, sticky="nsew") 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_1).grid(row=i, padx=10, pady=10)
customtkinter.CTkCheckBox(frame_2).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) customtkinter.CTkCheckBox(frame_3).grid(row=0, column=i, padx=10, pady=10)