From a3fb12f7cf1e293b3adf7997bb5480322c8f5112 Mon Sep 17 00:00:00 2001 From: Tom Schimansky Date: Sat, 2 Jul 2022 00:54:21 +0200 Subject: [PATCH] Fixed textvariable support for CTkCheckBox, CTkSwitch, CTkRadiobutton --- customtkinter/widgets/ctk_checkbox.py | 48 ++++----- customtkinter/widgets/ctk_radiobutton.py | 80 ++++++--------- customtkinter/widgets/ctk_slider.py | 3 - customtkinter/widgets/ctk_switch.py | 98 ++++++++----------- .../test_variables.py | 11 ++- 5 files changed, 100 insertions(+), 140 deletions(-) diff --git a/customtkinter/widgets/ctk_checkbox.py b/customtkinter/widgets/ctk_checkbox.py index 5471a3a..e4ed8a2 100644 --- a/customtkinter/widgets/ctk_checkbox.py +++ b/customtkinter/widgets/ctk_checkbox.py @@ -90,6 +90,19 @@ class CTkCheckBox(CTkBaseClass): self.canvas.bind("", self.on_leave) self.canvas.bind("", self.toggle) + self.text_label = tkinter.Label(master=self, + bd=0, + text=self.text, + justify=tkinter.LEFT, + font=self.apply_font_scaling(self.text_font), + textvariable=self.textvariable) + self.text_label.grid(row=0, column=2, padx=0, pady=0, sticky="w") + self.text_label["anchor"] = "w" + + self.text_label.bind("", self.on_enter) + self.text_label.bind("", self.on_leave) + self.text_label.bind("", self.toggle) + # set select state according to variable if self.variable is not None: self.variable_callback_name = self.variable.trace_add("write", self.variable_callback) @@ -153,32 +166,18 @@ class CTkCheckBox(CTkBaseClass): outline=ThemeManager.single_color(self.border_color, self._appearance_mode), fill=ThemeManager.single_color(self.border_color, self._appearance_mode)) - if self.text_label is None: - self.text_label = tkinter.Label(master=self, - bd=0, - text=self.text, - justify=tkinter.LEFT, - font=self.apply_font_scaling(self.text_font)) - self.text_label.grid(row=0, column=2, padx=0, pady=0, sticky="w") - self.text_label["anchor"] = "w" - - self.text_label.bind("", self.on_enter) - self.text_label.bind("", self.on_leave) - self.text_label.bind("", self.toggle) - if self.state == tkinter.DISABLED: self.text_label.configure(fg=(ThemeManager.single_color(self.text_color_disabled, self._appearance_mode))) else: self.text_label.configure(fg=ThemeManager.single_color(self.text_color, self._appearance_mode)) self.text_label.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode)) - self.set_text(self.text) - def configure(self, *args, **kwargs): require_redraw = False # some attribute changes require a call of self.draw() if "text" in kwargs: - self.set_text(kwargs["text"]) + self.text = kwargs["text"] + self.text_label.configure(text=self.text) del kwargs["text"] if "state" in kwargs: @@ -219,6 +218,11 @@ class CTkCheckBox(CTkBaseClass): self.function = kwargs["command"] del kwargs["command"] + if "textvariable" in kwargs: + self.textvariable = kwargs["textvariable"] + self.text_label.configure(textvariable=self.textvariable) + del kwargs["textvariable"] + if "variable" in kwargs: if self.variable is not None: self.variable.trace_remove("write", self.variable_callback_name) @@ -263,13 +267,6 @@ class CTkCheckBox(CTkBaseClass): if self.text_label is not None: self.text_label.configure(cursor="hand2") - def set_text(self, text): - self.text = text - if self.text_label is not None: - self.text_label.configure(text=self.text) - else: - sys.stderr.write("ERROR (CTkButton): Cant change text because checkbox has no text.") - def on_enter(self, event=0): if self.hover is True and self.state == tkinter.NORMAL: if self.check_state is True: @@ -347,10 +344,7 @@ class CTkCheckBox(CTkBaseClass): self.variable_callback_blocked = False if self.function is not None: - try: - self.function() - except: - pass + self.function() def get(self): return self.onvalue if self.check_state is True else self.offvalue diff --git a/customtkinter/widgets/ctk_radiobutton.py b/customtkinter/widgets/ctk_radiobutton.py index 779d9b8..1640551 100644 --- a/customtkinter/widgets/ctk_radiobutton.py +++ b/customtkinter/widgets/ctk_radiobutton.py @@ -1,6 +1,6 @@ import tkinter import sys -from typing import Callable, Union +from typing import Union from .ctk_canvas import CTkCanvas from ..theme_manager import ThemeManager @@ -86,6 +86,19 @@ class CTkRadioButton(CTkBaseClass): self.canvas.bind("", self.on_leave) self.canvas.bind("", self.invoke) + self.text_label = tkinter.Label(master=self, + bd=0, + text=self.text, + justify=tkinter.LEFT, + font=self.apply_font_scaling(self.text_font), + textvariable=self.textvariable) + self.text_label.grid(row=0, column=2, padx=0, pady=0, sticky="w") + self.text_label["anchor"] = "w" + + self.text_label.bind("", self.on_enter) + self.text_label.bind("", self.on_leave) + self.text_label.bind("", self.invoke) + self.draw() # initial draw self.set_cursor() @@ -134,19 +147,6 @@ class CTkRadioButton(CTkBaseClass): outline=ThemeManager.single_color(self.bg_color, self._appearance_mode), fill=ThemeManager.single_color(self.bg_color, self._appearance_mode)) - if self.text_label is None: - self.text_label = tkinter.Label(master=self, - bd=0, - text=self.text, - justify=tkinter.LEFT, - font=self.apply_font_scaling(self.text_font)) - self.text_label.grid(row=0, column=2, padx=0, pady=0, sticky="w") - self.text_label["anchor"] = "w" - - self.text_label.bind("", self.on_enter) - self.text_label.bind("", self.on_leave) - self.text_label.bind("", self.invoke) - if self.state == tkinter.DISABLED: self.text_label.configure(fg=ThemeManager.single_color(self.text_color_disabled, self._appearance_mode)) else: @@ -154,63 +154,58 @@ class CTkRadioButton(CTkBaseClass): self.text_label.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode)) - self.set_text(self.text) - def configure(self, *args, **kwargs): require_redraw = False # some attribute changes require a call of self.draw() if "text" in kwargs: - self.set_text(kwargs["text"]) - del kwargs["text"] + self.text = kwargs.pop("text") + self.text_label.configure(text=self.text) if "state" in kwargs: - self.state = kwargs["state"] + self.state = kwargs.pop("state") self.set_cursor() require_redraw = True - del kwargs["state"] if "fg_color" in kwargs: - self.fg_color = kwargs["fg_color"] + self.fg_color = kwargs.pop("fg_color") require_redraw = True - del kwargs["fg_color"] if "bg_color" in kwargs: - if kwargs["bg_color"] is None: + new_bg_color = kwargs.pop("bg_color") + if new_bg_color is None: self.bg_color = self.detect_color_of_master() else: - self.bg_color = kwargs["bg_color"] + self.bg_color = new_bg_color require_redraw = True - del kwargs["bg_color"] if "hover_color" in kwargs: - self.hover_color = kwargs["hover_color"] + self.hover_color = kwargs.pop("hover_color") require_redraw = True - del kwargs["hover_color"] if "text_color" in kwargs: - self.text_color = kwargs["text_color"] + self.text_color = kwargs.pop("text_color") require_redraw = True - del kwargs["text_color"] if "border_color" in kwargs: - self.border_color = kwargs["border_color"] + self.border_color = kwargs.pop("border_color") require_redraw = True - del kwargs["border_color"] if "border_width" in kwargs: - self.border_width = kwargs["border_width"] + self.border_width = kwargs.pop("border_width") require_redraw = True - del kwargs["border_width"] if "command" in kwargs: - self.function = kwargs["command"] - del kwargs["command"] + self.function = kwargs.pop("command") + + if "textvariable" in kwargs: + self.textvariable = kwargs.pop("textvariable") + self.text_label.configure(textvariable=self.textvariable) if "variable" in kwargs: if self.variable is not None: self.variable.trace_remove("write", self.variable_callback_name) - self.variable = kwargs["variable"] + self.variable = kwargs.pop("variable") if self.variable is not None and self.variable != "": self.variable_callback_name = self.variable.trace_add("write", self.variable_callback) @@ -221,8 +216,6 @@ class CTkRadioButton(CTkBaseClass): else: self.variable = None - del kwargs["variable"] - super().configure(*args, **kwargs) if require_redraw: @@ -250,13 +243,6 @@ class CTkRadioButton(CTkBaseClass): if self.text_label is not None: self.text_label.configure(cursor="hand2") - def set_text(self, text): - self.text = text - if self.text_label is not None: - self.text_label.configure(text=self.text) - else: - sys.stderr.write("ERROR (CTkButton): Cant change text because radiobutton has no text.") - def on_enter(self, event=0): if self.hover is True and self.state == tkinter.NORMAL: self.canvas.itemconfig("border_parts", @@ -288,10 +274,8 @@ class CTkRadioButton(CTkBaseClass): self.select() if self.function is not None: - try: + if self.function is not None: self.function() - except: - pass def select(self, from_variable_callback=False): self.check_state = True diff --git a/customtkinter/widgets/ctk_slider.py b/customtkinter/widgets/ctk_slider.py index 94889d3..91e9786 100644 --- a/customtkinter/widgets/ctk_slider.py +++ b/customtkinter/widgets/ctk_slider.py @@ -249,9 +249,6 @@ class CTkSlider(CTkBaseClass): self.draw(no_color_updates=False) - # if self.callback_function is not None and not from_variable_callback: - # self.callback_function(self.output_value) - if self.variable is not None and not from_variable_callback: self.variable_callback_blocked = True self.variable.set(round(self.output_value) if isinstance(self.variable, tkinter.IntVar) else self.output_value) diff --git a/customtkinter/widgets/ctk_switch.py b/customtkinter/widgets/ctk_switch.py index f8ec240..7a909be 100644 --- a/customtkinter/widgets/ctk_switch.py +++ b/customtkinter/widgets/ctk_switch.py @@ -62,11 +62,8 @@ class CTkSwitch(CTkBaseClass): self.onvalue = onvalue self.offvalue = offvalue - # if self.corner_radius < self.button_corner_radius: - # self.corner_radius = self.button_corner_radius - # callback and control variables - self.callback_function = command + self.function = command self.variable: tkinter.Variable = variable self.variable_callback_blocked = False self.variable_callback_name = None @@ -94,6 +91,19 @@ class CTkSwitch(CTkBaseClass): self.canvas.bind("", self.on_leave) self.canvas.bind("", self.toggle) + self.text_label = tkinter.Label(master=self, + bd=0, + text=self.text, + justify=tkinter.LEFT, + font=self.apply_font_scaling(self.text_font), + textvariable=self.textvariable) + self.text_label.grid(row=0, column=2, padx=0, pady=0, sticky="w") + self.text_label["anchor"] = "w" + + self.text_label.bind("", self.on_enter) + self.text_label.bind("", self.on_leave) + self.text_label.bind("", self.toggle) + self.draw() # initial draw self.set_cursor() @@ -186,22 +196,6 @@ class CTkSwitch(CTkBaseClass): self.canvas.itemconfig("slider_parts", fill=ThemeManager.single_color(self.button_color, self._appearance_mode), outline=ThemeManager.single_color(self.button_color, self._appearance_mode)) - if self.text_label is None: - self.text_label = tkinter.Label(master=self, - bd=0, - text=self.text, - justify=tkinter.LEFT, - font=self.apply_font_scaling(self.text_font)) - self.text_label.grid(row=0, column=2, padx=0, pady=0, sticky="w") - self.text_label["anchor"] = "w" - - self.text_label.bind("", self.on_enter) - self.text_label.bind("", self.on_leave) - self.text_label.bind("", self.toggle) - - if self.textvariable is not None: - self.text_label.configure(textvariable=self.textvariable) - if self.state == tkinter.DISABLED: self.text_label.configure(fg=(ThemeManager.single_color(self.text_color_disabled, self._appearance_mode))) else: @@ -209,13 +203,6 @@ class CTkSwitch(CTkBaseClass): self.text_label.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode)) - self.set_text(self.text) - - def set_text(self, text): - self.text = text - if self.text_label is not None: - self.text_label.configure(text=self.text) - def toggle(self, event=None): if self.state is not tkinter.DISABLED: if self.check_state is True: @@ -225,8 +212,8 @@ class CTkSwitch(CTkBaseClass): self.draw(no_color_updates=True) - if self.callback_function is not None: - self.callback_function() + if self.function is not None: + self.function() if self.variable is not None: self.variable_callback_blocked = True @@ -244,8 +231,8 @@ class CTkSwitch(CTkBaseClass): self.variable.set(self.onvalue) self.variable_callback_blocked = False - if self.callback_function is not None: - self.callback_function() + if self.function is not None: + self.function() def deselect(self, from_variable_callback=False): if self.state is not tkinter.DISABLED or from_variable_callback: @@ -258,8 +245,8 @@ class CTkSwitch(CTkBaseClass): self.variable.set(self.offvalue) self.variable_callback_blocked = False - if self.callback_function is not None: - self.callback_function() + if self.function is not None: + self.function() def get(self): return self.onvalue if self.check_state is True else self.offvalue @@ -286,66 +273,63 @@ class CTkSwitch(CTkBaseClass): def configure(self, *args, **kwargs): require_redraw = False # some attribute changes require a call of self.draw() at the end + if "text" in kwargs: + self.text = kwargs.pop("text") + self.text_label.configure(text=self.text) + if "state" in kwargs: - self.state = kwargs["state"] + self.state = kwargs.pop("state") self.set_cursor() require_redraw = True - del kwargs["state"] if "fg_color" in kwargs: - self.fg_color = kwargs["fg_color"] + self.fg_color = kwargs.pop("fg_color") require_redraw = True - del kwargs["fg_color"] if "bg_color" in kwargs: - if kwargs["bg_color"] is None: + new_bg_color = kwargs.pop("bg_color") + if new_bg_color is None: self.bg_color = self.detect_color_of_master() else: - self.bg_color = kwargs["bg_color"] + self.bg_color = new_bg_color require_redraw = True - del kwargs["bg_color"] if "progress_color" in kwargs: - if kwargs["progress_color"] is None: + new_progress_color = kwargs.pop("progress_color") + if new_progress_color is None: self.progress_color = self.fg_color else: - self.progress_color = kwargs["progress_color"] + self.progress_color = new_progress_color require_redraw = True - del kwargs["progress_color"] if "button_color" in kwargs: - self.button_color = kwargs["button_color"] + self.button_color = kwargs.pop("button_color") require_redraw = True - del kwargs["button_color"] if "button_hover_color" in kwargs: - self.button_hover_color = kwargs["button_hover_color"] + self.button_hover_color = kwargs.pop("button_hover_color") require_redraw = True - del kwargs["button_hover_color"] if "border_color" in kwargs: - self.border_color = kwargs["border_color"] + self.border_color = kwargs.pop("border_color") require_redraw = True - del kwargs["border_color"] if "border_width" in kwargs: - self.border_width = kwargs["border_width"] + self.border_width = kwargs.pop("border_width") require_redraw = True - del kwargs["border_width"] if "command" in kwargs: - self.callback_function = kwargs["command"] - del kwargs["command"] + self.function = kwargs.pop("command") if "textvariable" in kwargs: - self.text_label.configure(textvariable=kwargs["textvariable"]) - del kwargs["textvariable"] + self.textvariable = kwargs.pop("textvariable") + self.text_label.configure(textvariable=self.textvariable) if "variable" in kwargs: if self.variable is not None: self.variable.trace_remove("write", self.variable_callback_name) - self.variable = kwargs["variable"] + self.variable = kwargs.pop("variable") if self.variable is not None and self.variable != "": self.variable_callback_name = self.variable.trace_add("write", self.variable_callback) @@ -356,8 +340,6 @@ class CTkSwitch(CTkBaseClass): else: self.variable = None - del kwargs["variable"] - super().configure(*args, **kwargs) if require_redraw: diff --git a/test/manual_integration_tests/test_variables.py b/test/manual_integration_tests/test_variables.py index 528bd60..1e9d173 100644 --- a/test/manual_integration_tests/test_variables.py +++ b/test/manual_integration_tests/test_variables.py @@ -5,7 +5,7 @@ TEST_CONFIGURE = True TEST_REMOVING = False app = customtkinter.CTk() # create CTk window like you do with the Tk window (you can also use normal tkinter.Tk window) -app.geometry("400x800") +app.geometry("400x900") app.title("Tkinter Variable Test") txt_var = tkinter.StringVar(value="") @@ -46,7 +46,7 @@ if TEST_CONFIGURE: progress_1.configure(variable=int_var) if TEST_REMOVING: progress_1.configure(variable="") check_var = tkinter.StringVar(value="on") -check_1 = customtkinter.CTkCheckBox(app, text="check 1", variable=check_var, onvalue="on", offvalue="off") +check_1 = customtkinter.CTkCheckBox(app, text="check 1", variable=check_var, onvalue="on", offvalue="off", textvariable=txt_var) check_1.pack(pady=15) if TEST_CONFIGURE: check_1.configure(variable=check_var) if TEST_REMOVING: check_1.configure(variable="") @@ -67,8 +67,8 @@ def switch_event(): s_var = tkinter.StringVar(value="on") switch_1 = customtkinter.CTkSwitch(master=app, variable=s_var, textvariable=s_var, onvalue="on", offvalue="off", command=switch_event) switch_1.pack(pady=20, padx=10) -switch_1 = customtkinter.CTkSwitch(master=app, variable=s_var, textvariable=s_var, onvalue="on", offvalue="off") -switch_1.pack(pady=20, padx=10) +switch_2 = customtkinter.CTkSwitch(master=app, variable=s_var, textvariable=s_var, onvalue="on", offvalue="off") +switch_2.pack(pady=20, padx=10) optionmenu_var = tkinter.StringVar(value="test") optionmenu_1 = customtkinter.CTkOptionMenu(master=app, variable=optionmenu_var, values=["Option 1", "Option 2", "Option 3"]) @@ -77,4 +77,7 @@ combobox_1 = customtkinter.CTkComboBox(master=app, values=["Option 1", "Option 2 combobox_1.pack(pady=20, padx=10) combobox_1.configure(variable=optionmenu_var) +radio_1 = customtkinter.CTkRadioButton(app, textvariable=txt_var) +radio_1.pack(pady=20, padx=10) + app.mainloop()