From 02abcac5588e76bae38cf99d34bf576496226514 Mon Sep 17 00:00:00 2001 From: Tom Schimansky Date: Sat, 6 Nov 2021 00:35:07 +0100 Subject: [PATCH] added compound to CTkButton, restructured some code in CTkButton for better readability --- Readme.md | 1 + customtkinter/customtkinter_button.py | 126 +++++++++---------- customtkinter/customtkinter_color_manager.py | 4 +- examples/complex_example_other_style.py | 16 +-- examples/simple_example_images.py | 24 ++-- 5 files changed, 87 insertions(+), 84 deletions(-) diff --git a/Readme.md b/Readme.md index 4f20423..46f1555 100644 --- a/Readme.md +++ b/Readme.md @@ -161,6 +161,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 +compound | set image orientation if image and text are given ("top", "left", "bottom", "right") state | tkinter.NORMAL (standard) or tkinter.DISABLED (not clickable, darker color) CTkButton Methods: diff --git a/customtkinter/customtkinter_button.py b/customtkinter/customtkinter_button.py index 52ce305..9f9f1fd 100644 --- a/customtkinter/customtkinter_button.py +++ b/customtkinter/customtkinter_button.py @@ -24,11 +24,13 @@ class CTkButton(tkinter.Frame): text="CTkButton", hover=True, image=None, + compound=tkinter.LEFT, state=tkinter.NORMAL, *args, **kwargs): super().__init__(*args, **kwargs) AppearanceModeTracker.add(self.set_appearance_mode) + self.appearance_mode = AppearanceModeTracker.get_mode() # 0: "Light" 1: "Dark" self.configure_basic_grid() @@ -40,13 +42,10 @@ class CTkButton(tkinter.Frame): else: self.bg_color = bg_color - self.fg_color = self.bg_color if fg_color is None else fg_color self.hover_color = self.bg_color if hover_color is None else hover_color - + self.fg_color = self.bg_color if fg_color is None else fg_color self.fg_color = self.bg_color if self.fg_color is None else self.fg_color - self.border_color = border_color - self.appearance_mode = AppearanceModeTracker.get_mode() # 0: "Light" 1: "Dark" self.width = width self.height = height @@ -81,17 +80,18 @@ class CTkButton(tkinter.Frame): self.state = state self.hover = hover self.image = image + self.compound = compound self.configure(width=self.width, height=self.height) if sys.platform == "darwin" and self.function is not None: - self.configure(cursor="pointinghand") + self.configure(cursor="pointinghand") # other cursor when hovering over button with command self.canvas = tkinter.Canvas(master=self, highlightthicknes=0, width=self.width, height=self.height) - self.canvas.grid(row=0, column=0) + self.canvas.grid(row=0, column=0, rowspan=2, columnspan=2) if self.hover is True: self.canvas.bind("", self.on_enter) @@ -110,9 +110,11 @@ class CTkButton(tkinter.Frame): self.draw() def configure_basic_grid(self): - # Configuration of a basic grid in which all elements of CTkButtons are centered on one row and one column + # Configuration of a basic grid (2x2) in which all elements of CTkButtons are centered on one row and one column self.grid_rowconfigure(0, weight=1) self.grid_columnconfigure(0, weight=1) + self.grid_rowconfigure(1, weight=1) + self.grid_columnconfigure(1, weight=1) def update_dimensions(self, event): # We update the dimensions of the internal elements of CTkButton Widget @@ -125,15 +127,10 @@ class CTkButton(tkinter.Frame): self.canvas.delete("all") self.canvas_fg_parts = [] self.canvas_border_parts = [] + self.canvas.configure(bg=CTkColorManager.single_color(self.bg_color, self.appearance_mode)) - if type(self.bg_color) == tuple and len(self.bg_color) == 2: - self.canvas.configure(bg=self.bg_color[self.appearance_mode]) - else: - self.canvas.configure(bg=self.bg_color) - - # border button parts + # create border button parts if self.border_width > 0: - if self.corner_radius > 0: self.canvas_border_parts.append(self.canvas.create_oval(0, 0, @@ -161,8 +158,7 @@ class CTkButton(tkinter.Frame): self.width - self.corner_radius, self.height)) - # inner button parts - + # create inner button parts if self.corner_radius > 0: self.canvas_fg_parts.append(self.canvas.create_oval(self.border_width, self.border_width, @@ -190,80 +186,76 @@ class CTkButton(tkinter.Frame): self.width - self.border_width, self.height - self.inner_corner_radius - self.border_width)) + # set color for inner button parts for part in self.canvas_fg_parts: - if type(self.fg_color) == tuple and len(self.fg_color) == 2: - 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) + if self.state == tkinter.DISABLED: + self.canvas.itemconfig(part, + fill=CTkColorManager.darken_hex_color(CTkColorManager.single_color(self.fg_color, self.appearance_mode)), + width=0) else: - 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) + self.canvas.itemconfig(part, fill=CTkColorManager.single_color(self.fg_color, self.appearance_mode), width=0) + # set color for the button border parts (outline) for part in self.canvas_border_parts: - if type(self.border_color) == tuple and len(self.border_color) == 2: - self.canvas.itemconfig(part, fill=self.border_color[self.appearance_mode], width=0) - else: - self.canvas.itemconfig(part, fill=self.border_color, outline=self.border_color, width=0) + self.canvas.itemconfig(part, fill=CTkColorManager.single_color(self.border_color, self.appearance_mode), width=0) - # no image provided - if self.image is None: - self.text_label = tkinter.Label(master=self, - text=self.text, - font=self.text_font) - self.text_label.grid(row=0, column=0, padx=self.corner_radius) + # create text label if text given + if self.text is not None and self.text != "": + self.text_label = tkinter.Label(master=self, text=self.text, font=self.text_font) 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) - if type(self.text_color) == tuple and len(self.text_color) == 2: - self.text_label.configure(fg=self.text_color[self.appearance_mode]) - else: - self.text_label.configure(fg=self.text_color) + # set text_label fg color (text color) + self.text_label.configure(fg=CTkColorManager.single_color(self.text_color, self.appearance_mode)) - if type(self.fg_color) == tuple and len(self.fg_color) == 2: - 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]) + # set text_label bg color (label color) + if self.state == tkinter.DISABLED: + self.text_label.configure(bg=CTkColorManager.darken_hex_color(CTkColorManager.single_color(self.fg_color, self.appearance_mode))) else: - 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.text_label.configure(bg=CTkColorManager.single_color(self.fg_color, self.appearance_mode)) self.set_text(self.text) - # use image for button - else: - self.image_label = tkinter.Label(master=self, - image=self.image) - self.image_label.grid(row=0, column=0) + # use image for button if given + if self.image is not None: + self.image_label = tkinter.Label(master=self, image=self.image) 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: - 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]) + # set image_label bg color (background color of label) + if self.state == tkinter.DISABLED: + self.image_label.configure(bg=CTkColorManager.darken_hex_color(CTkColorManager.single_color(self.fg_color, self.appearance_mode))) else: - 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) + self.image_label.configure(bg=CTkColorManager.single_color(self.fg_color, self.appearance_mode)) + + # create grid layout with just an image given + if self.image_label is not None and self.text_label is None: + self.image_label.grid(row=0, column=0, rowspan=2, columnspan=2) + + # create grid layout with just text given + if self.image_label is None and self.text_label is not None: + self.text_label.grid(row=0, column=0, padx=self.corner_radius, rowspan=2, columnspan=2) + + # create grid layout of image and text label in 2x2 grid system with given compound + if self.image_label is not None and self.text_label is not None: + if self.compound == tkinter.LEFT or self.compound == "left": + self.image_label.grid(row=0, column=0, padx=self.corner_radius, sticky="e", rowspan=2) + self.text_label.grid(row=0, column=1, padx=self.corner_radius, sticky="w", rowspan=2) + elif self.compound == tkinter.TOP or self.compound == "top": + self.image_label.grid(row=0, column=0, padx=self.corner_radius, sticky="s", columnspan=2) + self.text_label.grid(row=1, column=0, padx=self.corner_radius, sticky="n", columnspan=2) + elif self.compound == tkinter.RIGHT or self.compound == "right": + self.image_label.grid(row=0, column=1, padx=self.corner_radius, sticky="w", rowspan=2) + self.text_label.grid(row=0, column=0, padx=self.corner_radius, sticky="e", rowspan=2) + elif self.compound == tkinter.BOTTOM or self.compound == "bottom": + self.image_label.grid(row=1, column=0, padx=self.corner_radius, sticky="n", columnspan=2) + self.text_label.grid(row=0, column=0, padx=self.corner_radius, sticky="s", columnspan=2) def configure_color(self, bg_color=None, fg_color=None, hover_color=None, text_color=None): if bg_color is not None: diff --git a/customtkinter/customtkinter_color_manager.py b/customtkinter/customtkinter_color_manager.py index ef7a129..2db8d90 100644 --- a/customtkinter/customtkinter_color_manager.py +++ b/customtkinter/customtkinter_color_manager.py @@ -27,11 +27,11 @@ class CTkColorManager: return color @staticmethod - def rgb2hex(rgb_color: tuple): + def rgb2hex(rgb_color: tuple) -> str: return "#{:02x}{:02x}{:02x}".format(round(rgb_color[0]), round(rgb_color[1]), round(rgb_color[2])) @staticmethod - def hex2rgb(hex_color: str): + def hex2rgb(hex_color: str) -> tuple: return tuple(int(hex_color.strip("#")[i:i+2], 16) for i in (0, 2, 4)) @staticmethod diff --git a/examples/complex_example_other_style.py b/examples/complex_example_other_style.py index 54301c8..dbfa5cb 100644 --- a/examples/complex_example_other_style.py +++ b/examples/complex_example_other_style.py @@ -117,20 +117,20 @@ class App(tkinter.Tk): self.slider_1 = customtkinter.CTkSlider(master=self.frame_right, button_color=App.MAIN_COLOR, button_hover_color=App.MAIN_HOVER, - width=160, - height=16, - border_width=5.5, - command=self.progressbar.set) + width=160, + height=16, + border_width=5.5, + command=self.progressbar.set) self.slider_1.place(x=20, rely=0.6, anchor=tkinter.W) self.slider_1.set(0.3) self.slider_2 = customtkinter.CTkSlider(master=self.frame_right, button_color=App.MAIN_COLOR, button_hover_color=App.MAIN_HOVER, - width=160, - height=16, - border_width=5.5, - command=self.progressbar.set) + width=160, + height=16, + border_width=5.5, + command=self.progressbar.set) self.slider_2.place(x=20, rely=0.7, anchor=tkinter.W) self.slider_2.set(0.7) diff --git a/examples/simple_example_images.py b/examples/simple_example_images.py index c224c59..b0ebab4 100644 --- a/examples/simple_example_images.py +++ b/examples/simple_example_images.py @@ -9,7 +9,7 @@ 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.geometry("400x400") root_tk.title("CustomTkinter Test") @@ -21,18 +21,28 @@ def button_function(): settings_image = ImageTk.PhotoImage(Image.open(PATH + "/test_images/settings.png").resize((40, 40))) bell_image = ImageTk.PhotoImage(Image.open(PATH + "/test_images/bell.png").resize((40, 40))) -frame_1 = customtkinter.CTkFrame(master=root_tk, width=300, height=200, corner_radius=15) +frame_1 = customtkinter.CTkFrame(master=root_tk, width=300, height=350, corner_radius=15) frame_1.place(relx=0.5, rely=0.5, anchor=tkinter.CENTER) -# button with settings-image -button_1 = customtkinter.CTkButton(master=frame_1, image=settings_image, width=60, height=60, +# button with settings-image and no text +button_1 = customtkinter.CTkButton(master=frame_1, image=settings_image, text="", width=60, height=60, corner_radius=10, command=button_function) -button_1.place(relx=0.33, rely=0.5, anchor=tkinter.CENTER) +button_1.place(relx=0.1, rely=0.2, anchor=tkinter.W) -# button with bell-image +# button with bell-image and standard compound ("left") button_2 = customtkinter.CTkButton(master=frame_1, image=bell_image, width=60, height=60, corner_radius=10, command=button_function) -button_2.place(relx=0.66, rely=0.5, anchor=tkinter.CENTER) +button_2.place(relx=0.9, rely=0.2, anchor=tkinter.E) + +# button with bell-image and compound="bottom" +button_4 = customtkinter.CTkButton(master=frame_1, image=bell_image, text="bell_image", compound="bottom", + command=button_function, height=100) +button_4.place(relx=0.5, rely=0.55, relwidth=0.5, anchor=tkinter.CENTER) + +# button with settings-image and compound="right" +button_4 = customtkinter.CTkButton(master=frame_1, image=settings_image, text="bell_image", compound="right", + command=button_function, height=60) +button_4.place(relx=0.5, rely=0.85, relwidth=0.5, anchor=tkinter.CENTER) root_tk.mainloop() customtkinter.disable_macos_darkmode()