mirror of
https://github.com/TomSchimansky/CustomTkinter.git
synced 2023-08-10 21:13:13 +03:00
removed .config(), added kwargs managing and filtering, added kwargs exceptions, fixed cursor color for combobox
This commit is contained in:
parent
bfc42c25ef
commit
1374e04f04
@ -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
|
||||
|
@ -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")
|
||||
|
@ -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")
|
||||
|
@ -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")
|
||||
|
@ -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)
|
||||
|
@ -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")
|
||||
|
@ -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")
|
||||
|
@ -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")
|
||||
|
@ -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")
|
||||
|
@ -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")
|
||||
|
@ -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")
|
||||
|
@ -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")
|
||||
|
@ -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")
|
||||
|
@ -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")
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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):
|
||||
|
55
test/unit_tests/test_ctk_entry.py
Normal file
55
test/unit_tests/test_ctk_entry.py
Normal 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()
|
Loading…
Reference in New Issue
Block a user