removed .config(), added kwargs managing and filtering, added kwargs exceptions, fixed cursor color for combobox

This commit is contained in:
Tom Schimansky 2022-10-03 23:50:59 +02:00
parent bfc42c25ef
commit 1374e04f04
23 changed files with 228 additions and 141 deletions

View File

@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
ToDo:
- limit configuring attributes of overridden tk widgets
- remove bg and background support for CTk and CTkToplevel (to be done)
- optimize font attribute managing
- enforce font size in pixel
- enforce font to be tuple
## Unreleased - 2022-10-2
### Added
- added .cget() method to all widgets and windows

View File

@ -240,9 +240,6 @@ class CTkButton(CTkBaseClass):
padx=max(self._apply_widget_scaling(self._corner_radius), self._apply_widget_scaling(self._border_width)),
pady=(self._apply_widget_scaling(self._border_width), 2))
def config(self, *args, **kwargs):
return self.configure(*args, **kwargs)
def configure(self, require_redraw=False, **kwargs):
if "text" in kwargs:
self._text = kwargs.pop("text")

View File

@ -177,9 +177,6 @@ class CTkCheckBox(CTkBaseClass):
self._text_label.configure(bg=ThemeManager.single_color(self._bg_color, self._appearance_mode))
def config(self, *args, **kwargs):
return self.configure(*args, **kwargs)
def configure(self, require_redraw=False, **kwargs):
if "text" in kwargs:
self._text = kwargs.pop("text")

View File

@ -175,8 +175,10 @@ class CTkComboBox(CTkBaseClass):
self._entry.configure(bg=ThemeManager.single_color(self._fg_color, self._appearance_mode),
fg=ThemeManager.single_color(self._text_color, self._appearance_mode),
disabledbackground=ThemeManager.single_color(self._fg_color, self._appearance_mode),
disabledforeground=ThemeManager.single_color(self._text_color_disabled, self._appearance_mode),
disabledbackground=ThemeManager.single_color(self._fg_color, self._appearance_mode))
highlightcolor=ThemeManager.single_color(self._fg_color, self._appearance_mode),
insertbackground=ThemeManager.single_color(self._text_color, self._appearance_mode))
if self._state == tkinter.DISABLED:
self._canvas.itemconfig("dropdown_arrow",
@ -189,9 +191,6 @@ class CTkComboBox(CTkBaseClass):
self._dropdown_menu.open(self.winfo_rootx(),
self.winfo_rooty() + self._apply_widget_scaling(self._current_height + 0))
def config(self, *args, **kwargs):
return self.configure(*args, **kwargs)
def configure(self, require_redraw=False, **kwargs):
if "state" in kwargs:
self._state = kwargs.pop("state")

View File

@ -6,7 +6,7 @@ from ..theme_manager import ThemeManager
from ..draw_engine import DrawEngine
from .widget_base_class import CTkBaseClass
from .widget_helper_functions import *
from .widget_helper_functions import pop_from_dict_by_set
class CTkEntry(CTkBaseClass):
@ -15,13 +15,12 @@ class CTkEntry(CTkBaseClass):
For detailed information check out the documentation.
"""
_minimum_x_padding = 6 # minimum padding between tkinter entry and frame border
# attributes that are passed to and managed by the tkinter entry only:
_valid_tk_entry_attributes = {"exportselection", "font", "highlightbackground",
"highlightcolor", "insertbackground", "insertborderwidth",
"insertofftime", "insertontime", "insertwidth",
"justify", "selectbackground", "selectborderwidth",
"selectforeground", "show", "takefocus", "validate",
"validatecommand", "xscrollcommand"}
_valid_tk_entry_attributes = {"exportselection", "insertborderwidth", "insertofftime",
"insertontime", "insertwidth", "justify", "selectborderwidth",
"show", "takefocus", "validate", "validatecommand", "xscrollcommand"}
def __init__(self, *args,
width: int = 140,
@ -55,22 +54,24 @@ class CTkEntry(CTkBaseClass):
self._fg_color = ThemeManager.theme["color"]["entry"] if fg_color == "default_theme" else fg_color
self._text_color = ThemeManager.theme["color"]["text"] if text_color == "default_theme" else text_color
self._placeholder_text_color = ThemeManager.theme["color"]["entry_placeholder_text"] if placeholder_text_color == "default_theme" else placeholder_text_color
self._font = (ThemeManager.theme["text"]["font"], ThemeManager.theme["text"]["size"]) if font == "default_theme" else font
self._border_color = ThemeManager.theme["color"]["entry_border"] if border_color == "default_theme" else border_color
# shape
self._corner_radius = ThemeManager.theme["shape"]["button_corner_radius"] if corner_radius == "default_theme" else corner_radius
self._border_width = ThemeManager.theme["shape"]["entry_border_width"] if border_width == "default_theme" else border_width
# placeholder text
# text and state
self._font = (ThemeManager.theme["text"]["font"], ThemeManager.theme["text"]["size"]) if font == "default_theme" else font
self._is_focused: bool = True
self._placeholder_text = placeholder_text
self._placeholder_text_active = False
self._pre_placeholder_arguments = {} # some set arguments of the entry will be changed for placeholder and then set back
# textvariable
self._textvariable = textvariable
self._state = state
self._textvariable_callback_name: str = ""
if not (self._textvariable is None or self._textvariable is ""):
self._textvariable_callback_name = self._textvariable.trace_add("write", self._textvariable_callback)
self._canvas = CTkCanvas(master=self,
highlightthickness=0,
@ -86,10 +87,15 @@ class CTkEntry(CTkBaseClass):
font=self._apply_font_scaling(self._font),
state=self._state,
textvariable=self._textvariable,
**filter_dict_by_set(kwargs, self._valid_tk_entry_attributes))
self._entry.grid(column=0, row=0, sticky="nswe",
padx=self._apply_widget_scaling(self._corner_radius) if self._corner_radius >= 6 else self._apply_widget_scaling(6),
**pop_from_dict_by_set(kwargs, self._valid_tk_entry_attributes))
if self._corner_radius >= self._minimum_x_padding:
self._entry.grid(column=0, row=0, sticky="nswe", padx=min(self._apply_widget_scaling(self._corner_radius), self._current_height),
pady=(self._apply_widget_scaling(self._border_width), self._apply_widget_scaling(self._border_width + 1)))
else:
self._entry.grid(column=0, row=0, sticky="nswe", padx=self._apply_widget_scaling(self._minimum_x_padding),
pady=(self._apply_widget_scaling(self._border_width), self._apply_widget_scaling(self._border_width + 1)))
self._check_kwargs_empty(kwargs, raise_error=True)
super().bind('<Configure>', self._update_dimensions_event)
self._entry.bind('<FocusOut>', self._entry_focus_out)
@ -98,6 +104,10 @@ class CTkEntry(CTkBaseClass):
self._activate_placeholder()
self._draw()
def _textvariable_callback(self, var_name, index, mode):
if self._textvariable.get() == "":
self._activate_placeholder()
def _set_scaling(self, *args, **kwargs):
super()._set_scaling(*args, **kwargs)
@ -129,20 +139,20 @@ class CTkEntry(CTkBaseClass):
fill=ThemeManager.single_color(self._fg_color, self._appearance_mode),
outline=ThemeManager.single_color(self._fg_color, self._appearance_mode))
self._entry.configure(bg=ThemeManager.single_color(self._fg_color, self._appearance_mode),
disabledbackground=ThemeManager.single_color(self._fg_color, self._appearance_mode),
highlightcolor=ThemeManager.single_color(self._fg_color, self._appearance_mode),
fg=ThemeManager.single_color(self._text_color, self._appearance_mode),
disabledbackground=ThemeManager.single_color(self._fg_color, self._appearance_mode),
disabledforeground=ThemeManager.single_color(self._text_color, self._appearance_mode),
highlightcolor=ThemeManager.single_color(self._fg_color, self._appearance_mode),
insertbackground=ThemeManager.single_color(self._text_color, self._appearance_mode))
else:
self._canvas.itemconfig("inner_parts",
fill=ThemeManager.single_color(self._bg_color, self._appearance_mode),
outline=ThemeManager.single_color(self._bg_color, self._appearance_mode))
self._entry.configure(bg=ThemeManager.single_color(self._bg_color, self._appearance_mode),
disabledbackground=ThemeManager.single_color(self._bg_color, self._appearance_mode),
highlightcolor=ThemeManager.single_color(self._bg_color, self._appearance_mode),
fg=ThemeManager.single_color(self._text_color, self._appearance_mode),
disabledbackground=ThemeManager.single_color(self._bg_color, self._appearance_mode),
disabledforeground=ThemeManager.single_color(self._text_color, self._appearance_mode),
highlightcolor=ThemeManager.single_color(self._bg_color, self._appearance_mode),
insertbackground=ThemeManager.single_color(self._text_color, self._appearance_mode))
self._canvas.itemconfig("border_parts",
@ -152,12 +162,7 @@ class CTkEntry(CTkBaseClass):
if self._placeholder_text_active:
self._entry.config(fg=ThemeManager.single_color(self._placeholder_text_color, self._appearance_mode))
def config(self, *args, **kwargs):
return self.configure(*args, **kwargs)
def configure(self, require_redraw=False, **kwargs):
self._entry.configure(**filter_dict_by_set(kwargs, self._valid_tk_entry_attributes))
if "state" in kwargs:
self._state = kwargs.pop("state")
self._entry.configure(state=self._state)
@ -174,15 +179,16 @@ class CTkEntry(CTkBaseClass):
self._border_color = kwargs.pop("border_color")
require_redraw = True
if "border_width" in kwargs:
self._border_width = kwargs.pop("border_width")
require_redraw = True
if "corner_radius" in kwargs:
self._corner_radius = kwargs.pop("corner_radius")
if self._corner_radius * 2 > self._current_height:
self._corner_radius = self._current_height / 2
elif self._corner_radius * 2 > self._current_width:
self._corner_radius = self._current_width / 2
self._entry.grid(column=0, row=0, sticky="we", padx=self._apply_widget_scaling(self._corner_radius) if self._corner_radius >= 6 else self._apply_widget_scaling(6))
if self._corner_radius >= self._minimum_x_padding:
self._entry.grid(column=0, row=0, sticky="we", padx=min(self._apply_widget_scaling(self._corner_radius), self._current_height / 2))
else:
self._entry.grid(column=0, row=0, sticky="we", padx=self._apply_widget_scaling(self._minimum_x_padding))
require_redraw = True
if "width" in kwargs:
@ -213,11 +219,12 @@ class CTkEntry(CTkBaseClass):
if "show" in kwargs:
if self._placeholder_text_active:
self._pre_placeholder_arguments["show"] = kwargs.pop("show")
self._pre_placeholder_arguments["show"] = kwargs.pop("show") # remember show argument for when placeholder gets deactivated
else:
self._entry.configure(show=kwargs.pop("show"))
super().configure(require_redraw=require_redraw, **kwargs)
self._entry.configure(**pop_from_dict_by_set(kwargs, self._valid_tk_entry_attributes)) # configure Tkinter.Entry
super().configure(require_redraw=require_redraw, **kwargs) # configure CTkBaseClass
def cget(self, attribute_name: str) -> any:
if attribute_name == "corner_radius":
@ -238,22 +245,18 @@ class CTkEntry(CTkBaseClass):
return self._textvariable
elif attribute_name == "placeholder_text":
return self._placeholder_text
elif attribute_name == "text_font":
elif attribute_name == "font":
return self._font
elif attribute_name == "state":
return self._state
elif attribute_name in ["exportselection", "font", "highlightbackground",
"highlightcolor", "insertbackground",
"insertborderwidth", "insertofftime", "insertontime",
"insertwidth", "justify", "selectbackground",
"selectborderwidth", "selectforeground", "show",
"takefocus", "validate", "validatecommand",
"xscrollcommand"]:
return self._entry.cget(attribute_name)
elif attribute_name in self._valid_tk_entry_attributes:
return self._entry.cget(attribute_name) # cget of tkinter.Entry
else:
return super().cget(attribute_name)
def bind(self, *args, **kwargs):
self._entry.bind(*args, **kwargs)
def bind(self, sequence=None, command=None, add=None):
self._entry.bind(sequence, command, add)
def _activate_placeholder(self):
if self._entry.get() == "" and self._placeholder_text is not None and (self._textvariable is None or self._textvariable == ""):
@ -281,16 +284,16 @@ class CTkEntry(CTkBaseClass):
self._deactivate_placeholder()
self._is_focused = True
def delete(self, *args, **kwargs):
self._entry.delete(*args, **kwargs)
def delete(self, first_index, last_index=None):
self._entry.delete(first_index, last_index)
if not self._is_focused and self._entry.get() == "":
self._activate_placeholder()
def insert(self, *args, **kwargs):
def insert(self, index, string):
self._deactivate_placeholder()
return self._entry.insert(*args, **kwargs)
return self._entry.insert(index, string)
def get(self):
if self._placeholder_text_active:
@ -299,7 +302,40 @@ class CTkEntry(CTkBaseClass):
return self._entry.get()
def focus(self):
self._entry.focus()
return self._entry.focus()
def focus_force(self):
self._entry.focus_force()
return self._entry.focus_force()
def index(self, index):
return self._entry.index(index)
def icursor(self, index):
return self._entry.icursor(index)
def select_adjust(self, index):
return self._entry.select_adjust(index)
def select_from(self, index):
return self._entry.icursor(index)
def select_clear(self):
return self._entry.select_clear()
def select_present(self):
return self._entry.select_present()
def select_range(self, start_index, end_index):
return self._entry.select_range(start_index, end_index)
def select_to(self, index):
return self._entry.select_to(index)
def xview(self, index):
return self._entry.xview(index)
def xview_moveto(self, f):
return self._entry.xview_moveto(f)
def xview_scroll(self, number, what):
return self._entry.xview_scroll(number, what)

View File

@ -114,9 +114,6 @@ class CTkFrame(CTkBaseClass):
self._canvas.tag_lower("inner_parts")
self._canvas.tag_lower("border_parts")
def config(self, *args, **kwargs):
return self.configure(*args, **kwargs)
def configure(self, require_redraw=False, **kwargs):
if "fg_color" in kwargs:
self._fg_color = kwargs.pop("fg_color")

View File

@ -115,9 +115,6 @@ class CTkLabel(CTkBaseClass):
self._canvas.configure(bg=ThemeManager.single_color(self._bg_color, self._appearance_mode))
def config(self, *args, **kwargs):
return self.configure(*args, **kwargs)
def configure(self, require_redraw=False, **kwargs):
if "anchor" in kwargs:
self._anchor = kwargs.pop("anchor")

View File

@ -193,9 +193,6 @@ class CTkOptionMenu(CTkBaseClass):
self._canvas.update_idletasks()
def config(self, *args, **kwargs):
return self.configure(*args, **kwargs)
def configure(self, require_redraw=False, **kwargs):
if "state" in kwargs:
self._state = kwargs.pop("state")

View File

@ -152,9 +152,6 @@ class CTkProgressBar(CTkBaseClass):
fill=ThemeManager.single_color(self._progress_color, self._appearance_mode),
outline=ThemeManager.single_color(self._progress_color, self._appearance_mode))
def config(self, *args, **kwargs):
return self.configure(*args, **kwargs)
def configure(self, require_redraw=False, **kwargs):
if "fg_color" in kwargs:
self._fg_color = kwargs.pop("fg_color")

View File

@ -158,9 +158,6 @@ class CTkRadioButton(CTkBaseClass):
self._text_label.configure(bg=ThemeManager.single_color(self._bg_color, self._appearance_mode))
def config(self, *args, **kwargs):
return self.configure(*args, **kwargs)
def configure(self, require_redraw=False, **kwargs):
if "text" in kwargs:
self._text = kwargs.pop("text")

View File

@ -150,9 +150,6 @@ class CTkScrollbar(CTkBaseClass):
self._canvas.update_idletasks()
def config(self, *args, **kwargs):
return self.configure(*args, **kwargs)
def configure(self, require_redraw=False, **kwargs):
if "fg_color" in kwargs:
self._fg_color = kwargs.pop("fg_color")

View File

@ -190,9 +190,6 @@ class CTkSlider(CTkBaseClass):
fill=ThemeManager.single_color(self._button_color, self._appearance_mode),
outline=ThemeManager.single_color(self._button_color, self._appearance_mode))
def config(self, *args, **kwargs):
return self.configure(*args, **kwargs)
def configure(self, require_redraw=False, **kwargs):
if "state" in kwargs:
self._state = kwargs.pop("state")

View File

@ -207,9 +207,6 @@ class CTkSwitch(CTkBaseClass):
self._text_label.configure(bg=ThemeManager.single_color(self._bg_color, self._appearance_mode))
def config(self, *args, **kwargs):
return self.configure(*args, **kwargs)
def configure(self, require_redraw=False, **kwargs):
if "text" in kwargs:
self._text = kwargs.pop("text")

View File

@ -118,9 +118,6 @@ class CTkTextbox(CTkBaseClass):
self._canvas.tag_lower("inner_parts")
self._canvas.tag_lower("border_parts")
def config(self, *args, **kwargs):
return self.configure(*args, **kwargs)
def configure(self, require_redraw=False, **kwargs):
if "fg_color" in kwargs:
self._fg_color = kwargs.pop("fg_color")

View File

@ -105,9 +105,6 @@ class DropdownMenu(tkinter.Menu):
else: # Linux
self.tk_popup(int(x), int(y))
def config(self, **kwargs):
return self.configure(**kwargs)
def configure(self, **kwargs):
if "fg_color" in kwargs:
self._fg_color = kwargs.pop("fg_color")

View File

@ -15,7 +15,7 @@ from ..appearance_mode_tracker import AppearanceModeTracker
from ..scaling_tracker import ScalingTracker
from ..theme_manager import ThemeManager
from .widget_helper_functions import filter_dict_by_set
from .widget_helper_functions import pop_from_dict_by_set
class CTkBaseClass(tkinter.Frame):
@ -23,7 +23,7 @@ class CTkBaseClass(tkinter.Frame):
appearance_mode changes, scaling, bg changes of master if master is not a CTk widget """
# attributes that are passed to and managed by the tkinter frame only:
_valid_tk_frame_attributes = {"cursor"}
_valid_tk_frame_attributes = {"cursor", "master"}
def __init__(self, *args,
width: int,
@ -32,7 +32,10 @@ class CTkBaseClass(tkinter.Frame):
bg_color: Union[str, tuple] = None,
**kwargs):
super().__init__(*args, width=width, height=height, **kwargs) # set desired size of underlying tkinter.Frame
super().__init__(*args, width=width, height=height, **pop_from_dict_by_set(kwargs, self._valid_tk_frame_attributes))
# check if kwargs is empty, if not raise error for unsupported arguments
self._check_kwargs_empty(kwargs, raise_error=True)
# dimensions
self._current_width = width # _current_width and _current_height in pixel, represent current size of the widget
@ -85,6 +88,18 @@ class CTkBaseClass(tkinter.Frame):
self.master.config = new_configure
self.master.configure = new_configure
@staticmethod
def _check_kwargs_empty(kwargs_dict, raise_error=False) -> bool:
""" returns True if kwargs are empty, False otherwise, raises error if not empty """
if len(kwargs_dict) > 0:
if raise_error:
raise ValueError(f"{list(kwargs_dict.keys())} are not supported arguments. Look at the documentation for supported arguments.")
else:
return True
else:
return False
def destroy(self):
AppearanceModeTracker.remove(self._set_appearance_mode)
super().destroy()
@ -127,10 +142,10 @@ class CTkBaseClass(tkinter.Frame):
pass
def config(self, *args, **kwargs):
return self.configure(*args, **kwargs)
raise AttributeError("'config' is not implemented for CTk widgets. For consistency, always use 'configure' instead.")
def configure(self, require_redraw=False, **kwargs):
""" basic configure with _bg_color support, to be overridden """
""" basic configure with bg_color support, calls configure of tkinter.Frame, calls draw() in the end """
if "bg_color" in kwargs:
new_bg_color = kwargs.pop("bg_color")
@ -140,21 +155,28 @@ class CTkBaseClass(tkinter.Frame):
self._bg_color = new_bg_color
require_redraw = True
super().configure(**filter_dict_by_set(kwargs, self._valid_tk_frame_attributes))
super().configure(**pop_from_dict_by_set(kwargs, self._valid_tk_frame_attributes)) # configure tkinter.Frame
# if there are still items in the kwargs dict, raise ValueError
self._check_kwargs_empty(kwargs, raise_error=True)
if require_redraw:
self._draw()
def cget(self, key: str):
if key == "bg_color":
def cget(self, attribute_name: str):
""" basic cget with bg_color, width, height support, calls cget of tkinter.Frame """
if attribute_name == "bg_color":
return self._bg_color
elif key == "width":
elif attribute_name == "width":
return self._desired_width
elif key == "height":
elif attribute_name == "height":
return self._desired_height
elif key in self._valid_tk_frame_attributes:
return super().cget(key)
elif attribute_name in self._valid_tk_frame_attributes:
return super().cget(attribute_name) # cget of tkinter.Frame
else:
raise ValueError(f"'{attribute_name}' is not a supported argument. Look at the documentation for supported arguments.")
def _update_dimensions_event(self, event):
# only redraw if dimensions changed (for performance), independent of scaling

View File

@ -1,7 +1,7 @@
def filter_dict_by_set(dictionary: dict, valid_keys: set):
""" remove all key value pairs, where key is not in valid_keys set """
""" create new dict with key value pairs of dictionary, where key is in valid_keys """
new_dictionary = {}
for key, value in dictionary.items():
@ -9,3 +9,14 @@ def filter_dict_by_set(dictionary: dict, valid_keys: set):
new_dictionary[key] = value
return new_dictionary
def pop_from_dict_by_set(dictionary: dict, valid_keys: set):
""" remove and create new dict with key value pairs of dictionary, where key is in valid_keys """
new_dictionary = {}
for key in list(dictionary.keys()):
if key in valid_keys:
new_dictionary[key] = dictionary.pop(key)
return new_dictionary

View File

@ -17,7 +17,7 @@ class CTkInputDialog:
For detailed information check out the documentation.
"""
def __init__(self,
def __init__(self, *args,
fg_color: Union[str, Tuple[str, str]] = "default_theme",
hover_color: Union[str, Tuple[str, str]] = "default_theme",
border_color: Union[str, Tuple[str, str]] = "default_theme",
@ -27,7 +27,13 @@ class CTkInputDialog:
text: str = "CTkDialog"):
self._appearance_mode = AppearanceModeTracker.get_mode() # 0: "Light" 1: "Dark"
if len(args) > 0 and master is None:
self.master = args[0]
elif master is not None:
self.master = master
else:
raise ValueError("master argument is missing")
self._window_bg_color = ThemeManager.theme["color"]["window_bg_color"]
self._fg_color = ThemeManager.theme["color"]["button"] if fg_color == "default_theme" else fg_color
@ -39,14 +45,15 @@ class CTkInputDialog:
self._height: int = len(text.split("\n")) * 20 + 150
self._text = text
self._toplevel_window = CTkToplevel()
self._toplevel_window = CTkToplevel(self.master)
self._toplevel_window.geometry(f"{280}x{self._height}")
self._toplevel_window.minsize(280, self._height)
self._toplevel_window.maxsize(280, self._height)
self._toplevel_window.title(title)
self._toplevel_window.lift()
self._toplevel_window.focus_force()
self._toplevel_window.grab_set()
self._toplevel_window.grab_set() # make other windows not clickable
self._toplevel_window.lift() # lift window on top
self._toplevel_window.attributes("-topmost", True) # stay on top
self._toplevel_window.protocol("WM_DELETE_WINDOW", self._on_closing)
self._toplevel_window.after(10, self._create_widgets) # create widgets with slight delay, to avoid white flickering of background
@ -99,25 +106,17 @@ class CTkInputDialog:
def _ok_event(self, event=None):
self._user_input = self._entry.get()
self._running = False
self._toplevel_window.grab_release()
self._toplevel_window.destroy()
def _on_closing(self):
self._running = False
self._toplevel_window.grab_release()
self._toplevel_window.destroy()
def _cancel_event(self):
self._running = False
self._toplevel_window.grab_release()
self._toplevel_window.destroy()
def get_input(self):
self._running = True
while self._running:
try:
self._toplevel_window.update()
except Exception:
return self._user_input
finally:
time.sleep(0.01)
time.sleep(0.05)
self._toplevel_window.destroy()
self.master.wait_window(self._toplevel_window)
return self._user_input

View File

@ -142,9 +142,9 @@ class CTk(tkinter.Tk):
super().mainloop(*args, **kwargs)
def resizable(self, *args, **kwargs):
super().resizable(*args, **kwargs)
self._last_resizable_args = (args, kwargs)
def resizable(self, width: bool = None, height: bool = None):
super().resizable(width, height)
self._last_resizable_args = ([], {"width": width, "height": height})
if sys.platform.startswith("win"):
if self._appearance_mode == 1:
@ -225,9 +225,6 @@ class CTk(tkinter.Tk):
else:
return value
def config(self, *args, **kwargs):
self.configure(*args, **kwargs)
def configure(self, *args, **kwargs):
bg_changed = False

View File

@ -161,9 +161,9 @@ class CTkToplevel(tkinter.Toplevel):
self._iconify_called_after_windows_set_titlebar_color = True
super().iconify()
def resizable(self, *args, **kwargs):
super().resizable(*args, **kwargs)
self._last_resizable_args = (args, kwargs)
def resizable(self, width: bool = None, height: bool = None):
super().resizable(width, height)
self._last_resizable_args = ([], {"width": width, "height": height})
if sys.platform.startswith("win"):
if self._appearance_mode == 1:
@ -189,9 +189,6 @@ class CTkToplevel(tkinter.Toplevel):
self._current_height = height
super().maxsize(self._apply_window_scaling(self._max_width), self._apply_window_scaling(self._max_height))
def config(self, *args, **kwargs):
self.configure(*args, **kwargs)
def configure(self, *args, **kwargs):
bg_changed = False

View File

@ -13,6 +13,8 @@ class App(customtkinter.CTk):
self.title("CustomTkinter complex_example.py")
self.geometry(f"{920}x{500}")
self.minsize(700, 400)
self.maxsize(1200, 700)
self.protocol("WM_DELETE_WINDOW", self.on_closing) # call .on_closing() when app gets closed
# configure grid layout (4x4)
@ -126,7 +128,7 @@ class App(customtkinter.CTk):
self.progressbar_1.start()
def open_input_dialog(self):
dialog = customtkinter.CTkInputDialog(master=None, text="Type in a number:", title="CTkInputDialog")
dialog = customtkinter.CTkInputDialog(master=self, text="Type in a number:", title="CTkInputDialog")
print("CTkInputDialog:", dialog.get_input())
def change_appearance_mode(self, new_appearance_mode: str):
@ -139,10 +141,8 @@ class App(customtkinter.CTk):
def sidebar_button_callback(self):
print("sidebar_button click")
self.entry.delete(0, tkinter.END)
def on_closing(self, event=0):
self.destroy()

View File

@ -76,14 +76,14 @@ class TestCTk():
def test_configure(self):
print(" -> test_configure: ", end="")
self.root_ctk.configure(bg="white")
assert self.root_ctk.fg_color == "white"
assert self.root_ctk.cget("fg_color") == "white"
self.root_ctk.configure(background="red")
assert self.root_ctk.fg_color == "red"
assert self.root_ctk.cget("fg_color") == "red"
assert self.root_ctk.cget("bg") == "red"
self.root_ctk.config(fg_color=("green", "#FFFFFF"))
assert self.root_ctk.fg_color == ("green", "#FFFFFF")
assert self.root_ctk.cget("fg_color") == ("green", "#FFFFFF")
print("successful")
def test_appearance_mode(self):

View File

@ -0,0 +1,55 @@
import customtkinter
import time
app = customtkinter.CTk()
entry_1 = customtkinter.CTkEntry(app, width=100, height=25)
entry_1.pack(padx=20, pady=20)
entry_2 = customtkinter.CTkEntry(app, width=100, height=25)
entry_2.pack(padx=20, pady=20)
txt_var = customtkinter.StringVar(value="test")
entry_1.configure(width=300,
height=35,
corner_radius=1000,
border_width=4,
bg_color="green",
fg_color=("red", "yellow"),
border_color="blue",
text_color=("brown", "green"),
placeholder_text_color="blue",
textvariable=txt_var,
placeholder_text="new_placholder",
font=("Times New Roman", -8, "bold"),
state="normal",
insertborderwidth=5,
insertwidth=10,
justify="right",
show="+")
assert entry_1.cget("width") == 300
assert entry_1.cget("height") == 35
assert entry_1.cget("corner_radius") == 1000
assert entry_1.cget("border_width") == 4
assert entry_1.cget("bg_color") == "green"
assert entry_1.cget("fg_color") == ("red", "yellow")
assert entry_1.cget("border_color") == "blue"
assert entry_1.cget("text_color") == ("brown", "green")
assert entry_1.cget("placeholder_text_color") == "blue"
assert entry_1.cget("textvariable") == txt_var
assert entry_1.cget("placeholder_text") == "new_placholder"
assert entry_1.cget("font") == ("Times New Roman", -8, "bold")
assert entry_1.cget("state") == "normal"
assert entry_1.cget("insertborderwidth") == 5
assert entry_1.cget("insertwidth") == 10
assert entry_1.cget("justify") == "right"
# assert entry_1.cget("show") == "+" # somehow does not work, maybe a tkinter bug?
def test_textvariable():
txt_var.set("test_2")
print(entry_1.get())
assert entry_1.get() == "test_2"
app.after(500, test_textvariable)
app.mainloop()