From ee5a19e55d34a4972637f6ce5b83e6a3510503d3 Mon Sep 17 00:00:00 2001 From: Tom Schimansky Date: Fri, 23 Jul 2021 00:44:13 +0200 Subject: [PATCH] button state functionality --- Readme.md | 5 + customtkinter/customtkinter_button.py | 143 +++++++++++++------ customtkinter/customtkinter_color_manager.py | 21 +++ examples/enable_disable_button_example.py | 35 +++++ examples/simple_example.py | 5 + 5 files changed, 164 insertions(+), 45 deletions(-) create mode 100644 examples/enable_disable_button_example.py diff --git a/Readme.md b/Readme.md index a7dbd07..613d24d 100644 --- a/Readme.md +++ b/Readme.md @@ -158,6 +158,7 @@ text_color | text color, tuple: (light_color, dark_color) or single color text_font | button text font, tuple: (font_name, size) hover | enable/disable hover effect: True, False image | put an image on the button, removes the text, must be class PhotoImage +state | tkinter.NORMAL (standard) or tkinter.DISABLED (not clickable, darker color) CTkButton Methods: ```python @@ -167,6 +168,10 @@ CTkButton.configure_color(bg_color=new_bg_color, fg_color=new_fg_color, hover_color=new_hover_color, text_color=new_text_color) + +CTkButton.configure(state=tkinter.DISABLED) +CTkButton.configure(state=tkinter.NORMAL) +button_state = CTkButton.state ``` diff --git a/customtkinter/customtkinter_button.py b/customtkinter/customtkinter_button.py index 91925d7..93f5d3c 100644 --- a/customtkinter/customtkinter_button.py +++ b/customtkinter/customtkinter_button.py @@ -24,6 +24,7 @@ class CTkButton(tkinter.Frame): text="CTkButton", hover=True, image=None, + state=tkinter.NORMAL, *args, **kwargs): super().__init__(*args, **kwargs) @@ -37,7 +38,7 @@ class CTkButton(tkinter.Frame): AppearanceModeTracker.add(self.set_appearance_mode) - if fg_color == None: + if fg_color is None: self.fg_color = self.bg_color else: self.fg_color = fg_color @@ -75,6 +76,7 @@ class CTkButton(tkinter.Frame): self.text_font = text_font self.function = command + self.state = state self.hover = hover self.image = image @@ -174,9 +176,18 @@ class CTkButton(tkinter.Frame): for part in self.canvas_fg_parts: if type(self.fg_color) == tuple and len(self.fg_color) == 2: - self.canvas.itemconfig(part, fill=self.fg_color[self.appearance_mode], width=0) + if self.state == tkinter.DISABLED: + self.canvas.itemconfig(part, + fill=CTkColorManager.darken_hex_color(self.fg_color[self.appearance_mode]), width=0) + else: + self.canvas.itemconfig(part, fill=self.fg_color[self.appearance_mode], width=0) else: - self.canvas.itemconfig(part, fill=self.fg_color, outline=self.fg_color, width=0) + if self.state == tkinter.DISABLED: + self.canvas.itemconfig(part, + fill=CTkColorManager.darken_hex_color(self.fg_color), + outline=self.fg_color, width=0) + else: + self.canvas.itemconfig(part, fill=self.fg_color, outline=self.fg_color, width=0) for part in self.canvas_border_parts: if type(self.border_color) == tuple and len(self.border_color) == 2: @@ -191,9 +202,8 @@ class CTkButton(tkinter.Frame): font=self.text_font) self.text_label.place(relx=0.5, rely=0.5, anchor=tkinter.CENTER) - if self.hover is True: - self.text_label.bind("", self.on_enter) - self.text_label.bind("", self.on_leave) + self.text_label.bind("", self.on_enter) + self.text_label.bind("", self.on_leave) self.text_label.bind("", self.clicked) self.text_label.bind("", self.clicked) @@ -204,9 +214,15 @@ class CTkButton(tkinter.Frame): self.text_label.configure(fg=self.text_color) if type(self.fg_color) == tuple and len(self.fg_color) == 2: - self.text_label.configure(bg=self.fg_color[self.appearance_mode]) + if self.state == tkinter.DISABLED: + self.text_label.configure(bg=CTkColorManager.darken_hex_color(self.fg_color[self.appearance_mode])) + else: + self.text_label.configure(bg=self.fg_color[self.appearance_mode]) else: - self.text_label.configure(bg=self.fg_color) + if self.state == tkinter.DISABLED: + self.text_label.configure(bg=CTkColorManager.darken_hex_color(self.fg_color)) + else: + self.text_label.configure(bg=self.fg_color) self.set_text(self.text) @@ -216,17 +232,22 @@ class CTkButton(tkinter.Frame): image=self.image) self.image_label.place(relx=0.5, rely=0.5, anchor=tkinter.CENTER) - if self.hover is True: - self.image_label.bind("", self.on_enter) - self.image_label.bind("", self.on_leave) + self.image_label.bind("", self.on_enter) + self.image_label.bind("", self.on_leave) self.image_label.bind("", self.clicked) self.image_label.bind("", self.clicked) if type(self.fg_color) == tuple and len(self.fg_color) == 2: - self.image_label.configure(bg=self.fg_color[self.appearance_mode]) + if self.state == tkinter.DISABLED: + self.image_label.configure(bg=CTkColorManager.darken_hex_color(self.fg_color[self.appearance_mode])) + else: + self.image_label.configure(bg=self.fg_color[self.appearance_mode]) else: - self.image_label.configure(bg=self.fg_color) + if self.state == tkinter.DISABLED: + self.image_label.configure(bg=CTkColorManager.darken_hex_color(self.fg_color)) + else: + self.image_label.configure(bg=self.fg_color) def configure_color(self, bg_color=None, fg_color=None, hover_color=None, text_color=None): if bg_color is not None: @@ -245,6 +266,35 @@ class CTkButton(tkinter.Frame): self.draw() + def config(self, *args, **kwargs): + self.configure(*args, **kwargs) + + def configure(self, *args, **kwargs): + if "text" in kwargs: + self.set_text(kwargs["text"]) + del kwargs["text"] + + if "state" in kwargs: + self.set_state(kwargs["state"]) + del kwargs["state"] + + super().configure(*args, **kwargs) + + def set_state(self, state): + self.state = state + + if self.state == tkinter.DISABLED: + self.hover = False + if sys.platform == "darwin" and self.function is not None: + self.configure(cursor="arrow") + + elif self.state == tkinter.NORMAL: + self.hover = True + if sys.platform == "darwin" and self.function is not None: + self.configure(cursor="pointinghand") + + self.draw() + def set_text(self, text): self.text = text if self.text_label is not None: @@ -260,47 +310,50 @@ class CTkButton(tkinter.Frame): sys.stderr.write("ERROR (CTkButton): Cant change image because button has no image.") def on_enter(self, event=0): - for part in self.canvas_fg_parts: - if type(self.hover_color) == tuple and len(self.hover_color) == 2: - self.canvas.itemconfig(part, fill=self.hover_color[self.appearance_mode], width=0) - else: - self.canvas.itemconfig(part, fill=self.hover_color, width=0) + if self.hover is True: + for part in self.canvas_fg_parts: + if type(self.hover_color) == tuple and len(self.hover_color) == 2: + self.canvas.itemconfig(part, fill=self.hover_color[self.appearance_mode], width=0) + else: + self.canvas.itemconfig(part, fill=self.hover_color, width=0) - if self.text_label is not None: - if type(self.hover_color) == tuple and len(self.hover_color) == 2: - self.text_label.configure(bg=self.hover_color[self.appearance_mode]) - else: - self.text_label.configure(bg=self.hover_color) + if self.text_label is not None: + if type(self.hover_color) == tuple and len(self.hover_color) == 2: + self.text_label.configure(bg=self.hover_color[self.appearance_mode]) + else: + self.text_label.configure(bg=self.hover_color) - if self.image_label is not None: - if type(self.hover_color) == tuple and len(self.hover_color) == 2: - self.image_label.configure(bg=self.hover_color[self.appearance_mode]) - else: - self.image_label.configure(bg=self.hover_color) + if self.image_label is not None: + if type(self.hover_color) == tuple and len(self.hover_color) == 2: + self.image_label.configure(bg=self.hover_color[self.appearance_mode]) + else: + self.image_label.configure(bg=self.hover_color) def on_leave(self, event=0): - for part in self.canvas_fg_parts: - if type(self.fg_color) == tuple and len(self.fg_color) == 2: - self.canvas.itemconfig(part, fill=self.fg_color[self.appearance_mode], width=0) - else: - self.canvas.itemconfig(part, fill=self.fg_color, width=0) + if self.hover is True: + for part in self.canvas_fg_parts: + if type(self.fg_color) == tuple and len(self.fg_color) == 2: + self.canvas.itemconfig(part, fill=self.fg_color[self.appearance_mode], width=0) + else: + self.canvas.itemconfig(part, fill=self.fg_color, width=0) - if self.text_label is not None: - if type(self.fg_color) == tuple and len(self.fg_color) == 2: - self.text_label.configure(bg=self.fg_color[self.appearance_mode]) - else: - self.text_label.configure(bg=self.fg_color) + if self.text_label is not None: + if type(self.fg_color) == tuple and len(self.fg_color) == 2: + self.text_label.configure(bg=self.fg_color[self.appearance_mode]) + else: + self.text_label.configure(bg=self.fg_color) - if self.image_label is not None: - if type(self.fg_color) == tuple and len(self.fg_color) == 2: - self.image_label.configure(bg=self.fg_color[self.appearance_mode]) - else: - self.image_label.configure(bg=self.fg_color) + if self.image_label is not None: + if type(self.fg_color) == tuple and len(self.fg_color) == 2: + self.image_label.configure(bg=self.fg_color[self.appearance_mode]) + else: + self.image_label.configure(bg=self.fg_color) def clicked(self, event=0): if self.function is not None: - self.function() - self.on_leave() + if self.state is not tkinter.DISABLED: + self.function() + self.on_leave() def set_appearance_mode(self, mode_string): if mode_string.lower() == "dark": diff --git a/customtkinter/customtkinter_color_manager.py b/customtkinter/customtkinter_color_manager.py index a631d73..b047442 100644 --- a/customtkinter/customtkinter_color_manager.py +++ b/customtkinter/customtkinter_color_manager.py @@ -11,6 +11,27 @@ class CTkColorManager: FRAME = ("#D4D5D6", "#3F3F3F") FRAME_2 = ("#BFBEC1", "#505050") + DARKEN_COLOR_FACTOR = 0.8 # used for generate color for disabled button + + @staticmethod + def rgb2hex(rgb_color): + return "#{:02x}{:02x}{:02x}".format(round(rgb_color[0]), round(rgb_color[1]), round(rgb_color[2])) + + @staticmethod + def hex2rgb(hex_color: str): + return tuple(int(hex_color.strip("#")[i:i+2], 16) for i in (0, 2, 4)) + + @staticmethod + def darken_hex_color(hex_color): + try: + rgb_color = CTkColorManager.hex2rgb(hex_color) + dark_rgb_color = (rgb_color[0] * CTkColorManager.DARKEN_COLOR_FACTOR, + rgb_color[1] * CTkColorManager.DARKEN_COLOR_FACTOR, + rgb_color[2] * CTkColorManager.DARKEN_COLOR_FACTOR) + return CTkColorManager.rgb2hex(dark_rgb_color) + except Exception as err: + return hex_color + @classmethod def set_theme_color(cls, hex_color, hex_color_hover): cls.MAIN = (hex_color, hex_color) diff --git a/examples/enable_disable_button_example.py b/examples/enable_disable_button_example.py new file mode 100644 index 0000000..2aeca82 --- /dev/null +++ b/examples/enable_disable_button_example.py @@ -0,0 +1,35 @@ +import tkinter +import customtkinter # <- import the CustomTkinter module + +customtkinter.enable_macos_darkmode() +customtkinter.set_appearance_mode("System") # Other: "Dark", "Light" + +root_tk = tkinter.Tk() # create the Tk window like you normally do +root_tk.geometry("400x240") +root_tk.title("CustomTkinter Test") + + +def change_button_2_state(): + if button_2.state == tkinter.NORMAL: + button_2.configure(state=tkinter.DISABLED) + elif button_2.state == tkinter.DISABLED: + button_2.configure(state=tkinter.NORMAL) + + +def button_2_click(): + print("button_2 clicked") + + +frame_1 = customtkinter.CTkFrame(master=root_tk, width=300, height=200, corner_radius=15) +frame_1.place(relx=0.5, rely=0.5, anchor=tkinter.CENTER) + +button_1 = customtkinter.CTkButton(master=frame_1, text="Disable/Enable Button_2", + corner_radius=10, command=change_button_2_state, width=200) +button_1.place(relx=0.5, rely=0.3, anchor=tkinter.CENTER) + +button_2 = customtkinter.CTkButton(master=frame_1, text="Button_2", + corner_radius=10, command=button_2_click) +button_2.place(relx=0.5, rely=0.7, anchor=tkinter.CENTER) + +root_tk.mainloop() +customtkinter.disable_macos_darkmode() diff --git a/examples/simple_example.py b/examples/simple_example.py index 087a572..50c7511 100644 --- a/examples/simple_example.py +++ b/examples/simple_example.py @@ -29,6 +29,11 @@ progressbar_1.place(relx=0.5, rely=0.25, anchor=tkinter.CENTER) button_1 = customtkinter.CTkButton(master=frame_1, corner_radius=10, command=button_function) button_1.place(relx=0.5, rely=0.5, anchor=tkinter.CENTER) +#button_1.configure(state="disabled") + +for child in button_1.winfo_children(): + child.configure(state='disable') + slider_1 = customtkinter.CTkSlider(master=frame_1, command=slider_function) slider_1.place(relx=0.5, rely=0.7, anchor=tkinter.CENTER)