added compound to CTkButton, restructured some code in CTkButton for better readability

This commit is contained in:
Tom Schimansky 2021-11-06 00:35:07 +01:00
parent ba1bf8c4b8
commit 02abcac558
5 changed files with 87 additions and 84 deletions

View File

@ -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) text_font | button text font, tuple: (font_name, size)
hover | enable/disable hover effect: True, False hover | enable/disable hover effect: True, False
image | put an image on the button, removes the text, must be class PhotoImage 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) state | tkinter.NORMAL (standard) or tkinter.DISABLED (not clickable, darker color)
CTkButton Methods: CTkButton Methods:

View File

@ -24,11 +24,13 @@ class CTkButton(tkinter.Frame):
text="CTkButton", text="CTkButton",
hover=True, hover=True,
image=None, image=None,
compound=tkinter.LEFT,
state=tkinter.NORMAL, state=tkinter.NORMAL,
*args, **kwargs): *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
AppearanceModeTracker.add(self.set_appearance_mode) AppearanceModeTracker.add(self.set_appearance_mode)
self.appearance_mode = AppearanceModeTracker.get_mode() # 0: "Light" 1: "Dark"
self.configure_basic_grid() self.configure_basic_grid()
@ -40,13 +42,10 @@ class CTkButton(tkinter.Frame):
else: else:
self.bg_color = bg_color 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.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.fg_color = self.bg_color if self.fg_color is None else self.fg_color
self.border_color = border_color self.border_color = border_color
self.appearance_mode = AppearanceModeTracker.get_mode() # 0: "Light" 1: "Dark"
self.width = width self.width = width
self.height = height self.height = height
@ -81,17 +80,18 @@ class CTkButton(tkinter.Frame):
self.state = state self.state = state
self.hover = hover self.hover = hover
self.image = image self.image = image
self.compound = compound
self.configure(width=self.width, height=self.height) self.configure(width=self.width, height=self.height)
if sys.platform == "darwin" and self.function is not None: 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, self.canvas = tkinter.Canvas(master=self,
highlightthicknes=0, highlightthicknes=0,
width=self.width, width=self.width,
height=self.height) 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: if self.hover is True:
self.canvas.bind("<Enter>", self.on_enter) self.canvas.bind("<Enter>", self.on_enter)
@ -110,9 +110,11 @@ class CTkButton(tkinter.Frame):
self.draw() self.draw()
def configure_basic_grid(self): 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_rowconfigure(0, weight=1)
self.grid_columnconfigure(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): def update_dimensions(self, event):
# We update the dimensions of the internal elements of CTkButton Widget # 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.delete("all")
self.canvas_fg_parts = [] self.canvas_fg_parts = []
self.canvas_border_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: # create border button parts
self.canvas.configure(bg=self.bg_color[self.appearance_mode])
else:
self.canvas.configure(bg=self.bg_color)
# border button parts
if self.border_width > 0: if self.border_width > 0:
if self.corner_radius > 0: if self.corner_radius > 0:
self.canvas_border_parts.append(self.canvas.create_oval(0, self.canvas_border_parts.append(self.canvas.create_oval(0,
0, 0,
@ -161,8 +158,7 @@ class CTkButton(tkinter.Frame):
self.width - self.corner_radius, self.width - self.corner_radius,
self.height)) self.height))
# inner button parts # create inner button parts
if self.corner_radius > 0: if self.corner_radius > 0:
self.canvas_fg_parts.append(self.canvas.create_oval(self.border_width, self.canvas_fg_parts.append(self.canvas.create_oval(self.border_width,
self.border_width, self.border_width,
@ -190,80 +186,76 @@ class CTkButton(tkinter.Frame):
self.width - self.border_width, self.width - self.border_width,
self.height - self.inner_corner_radius - 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: for part in self.canvas_fg_parts:
if type(self.fg_color) == tuple and len(self.fg_color) == 2: if self.state == tkinter.DISABLED:
if self.state == tkinter.DISABLED: self.canvas.itemconfig(part,
self.canvas.itemconfig(part, fill=CTkColorManager.darken_hex_color(CTkColorManager.single_color(self.fg_color, self.appearance_mode)),
fill=CTkColorManager.darken_hex_color(self.fg_color[self.appearance_mode]), width=0) width=0)
else:
self.canvas.itemconfig(part, fill=self.fg_color[self.appearance_mode], width=0)
else: else:
if self.state == tkinter.DISABLED: self.canvas.itemconfig(part, fill=CTkColorManager.single_color(self.fg_color, self.appearance_mode), width=0)
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)
# set color for the button border parts (outline)
for part in self.canvas_border_parts: for part in self.canvas_border_parts:
if type(self.border_color) == tuple and len(self.border_color) == 2: self.canvas.itemconfig(part, fill=CTkColorManager.single_color(self.border_color, self.appearance_mode), width=0)
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)
# no image provided # create text label if text given
if self.image is None: if self.text is not None and self.text != "":
self.text_label = tkinter.Label(master=self, self.text_label = tkinter.Label(master=self, text=self.text, font=self.text_font)
text=self.text,
font=self.text_font)
self.text_label.grid(row=0, column=0, padx=self.corner_radius)
self.text_label.bind("<Enter>", self.on_enter) self.text_label.bind("<Enter>", self.on_enter)
self.text_label.bind("<Leave>", self.on_leave) self.text_label.bind("<Leave>", self.on_leave)
self.text_label.bind("<Button-1>", self.clicked) self.text_label.bind("<Button-1>", self.clicked)
self.text_label.bind("<Button-1>", self.clicked) self.text_label.bind("<Button-1>", self.clicked)
if type(self.text_color) == tuple and len(self.text_color) == 2: # set text_label fg color (text color)
self.text_label.configure(fg=self.text_color[self.appearance_mode]) self.text_label.configure(fg=CTkColorManager.single_color(self.text_color, self.appearance_mode))
else:
self.text_label.configure(fg=self.text_color)
if type(self.fg_color) == tuple and len(self.fg_color) == 2: # set text_label bg color (label color)
if self.state == tkinter.DISABLED: if self.state == tkinter.DISABLED:
self.text_label.configure(bg=CTkColorManager.darken_hex_color(self.fg_color[self.appearance_mode])) self.text_label.configure(bg=CTkColorManager.darken_hex_color(CTkColorManager.single_color(self.fg_color, self.appearance_mode)))
else:
self.text_label.configure(bg=self.fg_color[self.appearance_mode])
else: else:
if self.state == tkinter.DISABLED: self.text_label.configure(bg=CTkColorManager.single_color(self.fg_color, self.appearance_mode))
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) self.set_text(self.text)
# use image for button # use image for button if given
else: if self.image is not None:
self.image_label = tkinter.Label(master=self, self.image_label = tkinter.Label(master=self, image=self.image)
image=self.image)
self.image_label.grid(row=0, column=0)
self.image_label.bind("<Enter>", self.on_enter) self.image_label.bind("<Enter>", self.on_enter)
self.image_label.bind("<Leave>", self.on_leave) self.image_label.bind("<Leave>", self.on_leave)
self.image_label.bind("<Button-1>", self.clicked) self.image_label.bind("<Button-1>", self.clicked)
self.image_label.bind("<Button-1>", self.clicked) self.image_label.bind("<Button-1>", self.clicked)
if type(self.fg_color) == tuple and len(self.fg_color) == 2: # set image_label bg color (background color of label)
if self.state == tkinter.DISABLED: if self.state == tkinter.DISABLED:
self.image_label.configure(bg=CTkColorManager.darken_hex_color(self.fg_color[self.appearance_mode])) self.image_label.configure(bg=CTkColorManager.darken_hex_color(CTkColorManager.single_color(self.fg_color, self.appearance_mode)))
else:
self.image_label.configure(bg=self.fg_color[self.appearance_mode])
else: else:
if self.state == tkinter.DISABLED: self.image_label.configure(bg=CTkColorManager.single_color(self.fg_color, self.appearance_mode))
self.image_label.configure(bg=CTkColorManager.darken_hex_color(self.fg_color))
else: # create grid layout with just an image given
self.image_label.configure(bg=self.fg_color) 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): def configure_color(self, bg_color=None, fg_color=None, hover_color=None, text_color=None):
if bg_color is not None: if bg_color is not None:

View File

@ -27,11 +27,11 @@ class CTkColorManager:
return color return color
@staticmethod @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])) return "#{:02x}{:02x}{:02x}".format(round(rgb_color[0]), round(rgb_color[1]), round(rgb_color[2]))
@staticmethod @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)) return tuple(int(hex_color.strip("#")[i:i+2], 16) for i in (0, 2, 4))
@staticmethod @staticmethod

View File

@ -117,20 +117,20 @@ class App(tkinter.Tk):
self.slider_1 = customtkinter.CTkSlider(master=self.frame_right, self.slider_1 = customtkinter.CTkSlider(master=self.frame_right,
button_color=App.MAIN_COLOR, button_color=App.MAIN_COLOR,
button_hover_color=App.MAIN_HOVER, button_hover_color=App.MAIN_HOVER,
width=160, width=160,
height=16, height=16,
border_width=5.5, border_width=5.5,
command=self.progressbar.set) command=self.progressbar.set)
self.slider_1.place(x=20, rely=0.6, anchor=tkinter.W) self.slider_1.place(x=20, rely=0.6, anchor=tkinter.W)
self.slider_1.set(0.3) self.slider_1.set(0.3)
self.slider_2 = customtkinter.CTkSlider(master=self.frame_right, self.slider_2 = customtkinter.CTkSlider(master=self.frame_right,
button_color=App.MAIN_COLOR, button_color=App.MAIN_COLOR,
button_hover_color=App.MAIN_HOVER, button_hover_color=App.MAIN_HOVER,
width=160, width=160,
height=16, height=16,
border_width=5.5, border_width=5.5,
command=self.progressbar.set) command=self.progressbar.set)
self.slider_2.place(x=20, rely=0.7, anchor=tkinter.W) self.slider_2.place(x=20, rely=0.7, anchor=tkinter.W)
self.slider_2.set(0.7) self.slider_2.set(0.7)

View File

@ -9,7 +9,7 @@ customtkinter.enable_macos_darkmode()
customtkinter.set_appearance_mode("System") # Other: "Dark", "Light" customtkinter.set_appearance_mode("System") # Other: "Dark", "Light"
root_tk = tkinter.Tk() # create the Tk window like you normally do 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") 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))) 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))) 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) frame_1.place(relx=0.5, rely=0.5, anchor=tkinter.CENTER)
# button with settings-image # button with settings-image and no text
button_1 = customtkinter.CTkButton(master=frame_1, image=settings_image, width=60, height=60, button_1 = customtkinter.CTkButton(master=frame_1, image=settings_image, text="", width=60, height=60,
corner_radius=10, command=button_function) 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, button_2 = customtkinter.CTkButton(master=frame_1, image=bell_image, width=60, height=60,
corner_radius=10, command=button_function) 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() root_tk.mainloop()
customtkinter.disable_macos_darkmode() customtkinter.disable_macos_darkmode()