From de33629e7d586953fddbcf8d85b3171277e63b0e Mon Sep 17 00:00:00 2001 From: Tom Schimansky Date: Thu, 7 Jul 2022 18:07:54 +0200 Subject: [PATCH] fixed entry placeholder for textvariables and added test_entry_placeholder.py --- customtkinter/widgets/ctk_button.py | 2 +- customtkinter/widgets/ctk_entry.py | 62 +++++++++++-------- customtkinter/widgets/ctk_switch.py | 2 - customtkinter/widgets/widget_base_class.py | 2 +- .../test_variables.py | 2 +- .../text_entry_placeholder.py | 27 ++++++++ 6 files changed, 66 insertions(+), 31 deletions(-) create mode 100644 test/manual_integration_tests/text_entry_placeholder.py diff --git a/customtkinter/widgets/ctk_button.py b/customtkinter/widgets/ctk_button.py index 570af4f..87eb9b1 100644 --- a/customtkinter/widgets/ctk_button.py +++ b/customtkinter/widgets/ctk_button.py @@ -1,6 +1,6 @@ import tkinter import sys -from typing import Union, Tuple, Callable +from typing import Union, Tuple, Callable, Literal from .ctk_canvas import CTkCanvas from ..theme_manager import ThemeManager diff --git a/customtkinter/widgets/ctk_entry.py b/customtkinter/widgets/ctk_entry.py index cc3ba3b..ee1218a 100644 --- a/customtkinter/widgets/ctk_entry.py +++ b/customtkinter/widgets/ctk_entry.py @@ -20,6 +20,7 @@ class CTkEntry(CTkBaseClass): width=140, height=28, state=tkinter.NORMAL, + textvariable: tkinter.Variable = None, **kwargs): # transfer basic functionality (bg_color, size, _appearance_mode, scaling) to CTkBaseClass @@ -28,23 +29,30 @@ class CTkEntry(CTkBaseClass): else: super().__init__(*args, bg_color=bg_color, width=width, height=height) + # configure grid system (1x1) self.grid_rowconfigure(0, weight=1) self.grid_columnconfigure(0, weight=1) + # color 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.text_font = (ThemeManager.theme["text"]["font"], ThemeManager.theme["text"]["size"]) if text_font == "default_theme" else text_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 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 - self.state = state + # textvariable + self.textvariable = textvariable - 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 + self.state = state self.canvas = CTkCanvas(master=self, highlightthickness=0, @@ -59,6 +67,7 @@ class CTkEntry(CTkBaseClass): highlightthickness=0, font=self.apply_font_scaling(self.text_font), state=self.state, + textvariable=self.textvariable, **kwargs) 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), @@ -68,12 +77,9 @@ class CTkEntry(CTkBaseClass): self.entry.bind('', self.entry_focus_out) self.entry.bind('', self.entry_focus_in) + self.set_placeholder() self.draw() - if self.placeholder_text is not None: - self.placeholder_text_active = True - self.set_placeholder() - def set_scaling(self, *args, **kwargs): super().set_scaling( *args, **kwargs) @@ -175,6 +181,10 @@ class CTkEntry(CTkBaseClass): self.placeholder_text_color = kwargs.pop("placeholder_text_color") require_redraw = True + if "textvariable" in kwargs: + self.textvariable = kwargs.pop("textvariable") + self.entry.configure(textvariable=self.textvariable) + if "show" in kwargs: if self.placeholder_text_active: self.pre_placeholder_arguments["show"] = kwargs.pop("show") @@ -189,26 +199,28 @@ class CTkEntry(CTkBaseClass): self.entry.configure(**kwargs) # pass remaining kwargs to entry def set_placeholder(self): - self.pre_placeholder_arguments = {"show": self.entry.cget("show")} - self.entry.config(fg=ThemeManager.single_color(self.placeholder_text_color, self._appearance_mode), show="") - self.entry.delete(0, tkinter.END) - self.entry.insert(0, self.placeholder_text) + if self.entry.get() == "" and self.placeholder_text is not None and (self.textvariable is None or self.textvariable == ""): + self.placeholder_text_active = True + + self.pre_placeholder_arguments = {"show": self.entry.cget("show")} + self.entry.config(fg=ThemeManager.single_color(self.placeholder_text_color, self._appearance_mode), show="") + self.entry.delete(0, tkinter.END) + self.entry.insert(0, self.placeholder_text) def clear_placeholder(self): - self.entry.config(fg=ThemeManager.single_color(self.text_color, self._appearance_mode)) - self.entry.delete(0, tkinter.END) - for argument, value in self.pre_placeholder_arguments.items(): - self.entry[argument] = value - - def entry_focus_out(self, event=None): - if self.entry.get() == "": - self.placeholder_text_active = True - self.set_placeholder() - - def entry_focus_in(self, event=None): if self.placeholder_text_active: self.placeholder_text_active = False - self.clear_placeholder() + + self.entry.config(fg=ThemeManager.single_color(self.text_color, self._appearance_mode)) + self.entry.delete(0, tkinter.END) + for argument, value in self.pre_placeholder_arguments.items(): + self.entry[argument] = value + + def entry_focus_out(self, event=None): + self.set_placeholder() + + def entry_focus_in(self, event=None): + self.clear_placeholder() def delete(self, *args, **kwargs): self.entry.delete(*args, **kwargs) @@ -218,9 +230,7 @@ class CTkEntry(CTkBaseClass): self.set_placeholder() def insert(self, *args, **kwargs): - if self.placeholder_text_active: - self.placeholder_text_active = False - self.clear_placeholder() + self.clear_placeholder() return self.entry.insert(*args, **kwargs) diff --git a/customtkinter/widgets/ctk_switch.py b/customtkinter/widgets/ctk_switch.py index dddc890..de64448 100644 --- a/customtkinter/widgets/ctk_switch.py +++ b/customtkinter/widgets/ctk_switch.py @@ -322,7 +322,5 @@ class CTkSwitch(CTkBaseClass): self.variable_callback_name = self.variable.trace_add("write", self.variable_callback) self.check_state = True if self.variable.get() == self.onvalue else False require_redraw = True - else: - self.variable = None super().configure(require_redraw=require_redraw, **kwargs) diff --git a/customtkinter/widgets/widget_base_class.py b/customtkinter/widgets/widget_base_class.py index 77e2e0c..cfd59e7 100644 --- a/customtkinter/widgets/widget_base_class.py +++ b/customtkinter/widgets/widget_base_class.py @@ -17,7 +17,7 @@ from ..theme_manager import ThemeManager class CTkBaseClass(tkinter.Frame): - """ Base class of every Ctk widget, handles the dimensions, bg_color, + """ Base class of every CTk widget, handles the dimensions, bg_color, appearance_mode changes, scaling, bg changes of master if master is not a CTk widget """ def __init__(self, diff --git a/test/manual_integration_tests/test_variables.py b/test/manual_integration_tests/test_variables.py index a8fb5df..04bb0dc 100644 --- a/test/manual_integration_tests/test_variables.py +++ b/test/manual_integration_tests/test_variables.py @@ -12,7 +12,7 @@ def checkbox_event(): print("checkbox_event") txt_var = tkinter.StringVar(value="") -entry_1 = customtkinter.CTkEntry(app, width=200, textvariable=txt_var) +entry_1 = customtkinter.CTkEntry(app, width=200, textvariable=txt_var, placeholder_text="placeholder") entry_1.pack(pady=15) txt_var.set("new text test") if TEST_CONFIGURE: entry_1.configure(textvariable=txt_var) diff --git a/test/manual_integration_tests/text_entry_placeholder.py b/test/manual_integration_tests/text_entry_placeholder.py new file mode 100644 index 0000000..42cb145 --- /dev/null +++ b/test/manual_integration_tests/text_entry_placeholder.py @@ -0,0 +1,27 @@ +import customtkinter + +app = customtkinter.CTk() # create CTk window like you do with the Tk window (you can also use normal tkinter.Tk window) +app.geometry("400x400") +app.title("test_entry_placeholder.py") + +str_var = customtkinter.StringVar(value="test") + +entry_1 = customtkinter.CTkEntry(app, placeholder_text="placeholder", textvariable=str_var) +entry_1.pack(pady=20) + +entry_2 = customtkinter.CTkEntry(app, placeholder_text="placeholder", textvariable=str_var) +entry_2.pack(pady=20) +entry_2.insert(0, "sdfjk ") +entry_2.delete(0, 2) + +entry_3 = customtkinter.CTkEntry(app, placeholder_text="placeholder") +entry_3.pack(pady=(40, 20)) +entry_3.insert(0, "sdfjk") +entry_3.delete(0, "end") + +entry_4 = customtkinter.CTkEntry(app, placeholder_text="password", show="*") +entry_4.pack(pady=(20, 20)) +entry_4.insert(0, "sdfjk") +entry_4.delete(0, 2) + +app.mainloop()