diff --git a/customtkinter/customtkinter_button.py b/customtkinter/customtkinter_button.py index c572a29..3b67693 100644 --- a/customtkinter/customtkinter_button.py +++ b/customtkinter/customtkinter_button.py @@ -23,7 +23,7 @@ class CTkButton(tkinter.Frame): width=120, height=30, corner_radius=8, - text_font=None, + text_font="default_theme", text_color="default_theme", text="CTkButton", hover=True, @@ -88,15 +88,7 @@ class CTkButton(tkinter.Frame): self.text = text self.text_label = None self.text_color = CTkThemeManager.TEXT_COLOR if text_color == "default_theme" else text_color - if text_font is None: - if sys.platform == "darwin": # macOS - self.text_font = ("Avenir", 13) - elif sys.platform.startswith("win"): # Windows - self.text_font = ("Segoe UI", -14) - else: - self.text_font = "TkDefaultFont" - else: - self.text_font = text_font + self.text_font = (CTkThemeManager.TEXT_FONT_NAME, CTkThemeManager.TEXT_FONT_SIZE) if text_font == "default_theme" else text_font # callback and hover functionality self.function = command @@ -160,21 +152,18 @@ class CTkButton(tkinter.Frame): def calc_optimal_corner_radius(user_corner_radius): if sys.platform == "darwin": return user_corner_radius # on macOS just use given value (canvas has Antialiasing) + elif sys.platform.startswith("win"): + return round(user_corner_radius) else: - draw_method = "font" - if draw_method == "shapes": - user_corner_radius = 0.5 * round(user_corner_radius / 0.5) # round to 0.5 steps + user_corner_radius = 0.5 * round(user_corner_radius / 0.5) # round to 0.5 steps - # make sure the value is always with .5 at the end for smoother corners - if user_corner_radius == 0: - return 0 - elif user_corner_radius % 1 == 0: - return user_corner_radius + 0.5 - else: - return user_corner_radius - - elif draw_method == "font": - return round(user_corner_radius) + # make sure the value is always with .5 at the end for smoother corners + if user_corner_radius == 0: + return 0 + elif user_corner_radius % 1 == 0: + return user_corner_radius + 0.5 + else: + return user_corner_radius def draw(self, no_color_updates=False): self.canvas.configure(bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode)) diff --git a/customtkinter/customtkinter_checkbox.py b/customtkinter/customtkinter_checkbox.py index 7fa0bcd..d942e98 100644 --- a/customtkinter/customtkinter_checkbox.py +++ b/customtkinter/customtkinter_checkbox.py @@ -20,8 +20,8 @@ class CTkCheckBox(tkinter.Frame): border_width=3, width=25, height=25, - corner_radius=4, - text_font=None, + corner_radius=6, + text_font="default_theme", text_color="default_theme", text="CTkCheckBox", hover=True, @@ -81,15 +81,7 @@ class CTkCheckBox(tkinter.Frame): self.text = text self.text_color = CTkThemeManager.TEXT_COLOR if text_color == "default_theme" else text_color - if text_font is None: - if sys.platform == "darwin": # macOS - self.text_font = ("Avenir", 13) - elif "win" in sys.platform: # Windows - self.text_font = ("Century Gothic", 11) - else: - self.text_font = ("TkDefaultFont") - else: - self.text_font = text_font + self.text_font = (CTkThemeManager.TEXT_FONT_NAME, CTkThemeManager.TEXT_FONT_SIZE) if text_font == "default_theme" else text_font self.function = command self.state = state @@ -149,6 +141,8 @@ class CTkCheckBox(tkinter.Frame): def calc_optimal_corner_radius(user_corner_radius): if sys.platform == "darwin": return user_corner_radius # on macOS just use given value (canvas has Antialiasing) + elif sys.platform.startswith("win"): + return round(user_corner_radius) else: user_corner_radius = 0.5 * round(user_corner_radius / 0.5) # round to 0.5 steps @@ -164,7 +158,7 @@ class CTkCheckBox(tkinter.Frame): if sys.platform == "darwin": self.draw_with_ovals_and_rects() elif sys.platform.startswith("win"): - self.draw_with_ovals_and_rects() + self.draw_with_font_shapes_and_rects() else: self.draw_with_ovals_and_rects() @@ -224,7 +218,7 @@ class CTkCheckBox(tkinter.Frame): # inner button parts if self.inner_corner_radius > 0: - if not self.canvas.find_withtag("inner_corner_part"): + if not self.canvas.find_withtag("inner_oval_1"): self.canvas.create_oval(0, 0, 0, 0, tags=("inner_oval_1", "inner_corner_part", "inner_parts")) self.canvas.create_oval(0, 0, 0, 0, tags=("inner_oval_2", "inner_corner_part", "inner_parts")) self.canvas.create_oval(0, 0, 0, 0, tags=("inner_oval_3", "inner_corner_part", "inner_parts")) @@ -241,7 +235,7 @@ class CTkCheckBox(tkinter.Frame): else: self.canvas.delete("inner_corner_part") # delete inner corner parts if not needed - if not self.canvas.find_withtag("inner_rectangle_part"): + if not self.canvas.find_withtag("inner_rectangle_1"): self.canvas.create_rectangle(0, 0, 0, 0, tags=("inner_rectangle_1", "inner_rectangle_part", "inner_parts")) self.canvas.create_rectangle(0, 0, 0, 0, tags=("inner_rectangle_2", "inner_rectangle_part", "inner_parts")) @@ -254,6 +248,86 @@ class CTkCheckBox(tkinter.Frame): self.width - self.border_width - 1, self.height - self.inner_corner_radius - self.border_width - 1)) + def draw_with_font_shapes_and_rects(self): + # border button parts + if self.border_width > 0: + if self.corner_radius > 0: + + if not self.canvas.find_withtag("border_oval_1_a"): + self.canvas.create_aa_circle(0, 0, 0, tags=("border_oval_1_a", "border_corner_part", "border_parts")) + self.canvas.create_aa_circle(0, 0, 0, tags=("border_oval_1_b", "border_corner_part", "border_parts"), angle=180) + self.canvas.create_aa_circle(0, 0, 0, tags=("border_oval_2_a", "border_corner_part", "border_parts")) + self.canvas.create_aa_circle(0, 0, 0, tags=("border_oval_2_b", "border_corner_part", "border_parts"), angle=180) + self.canvas.create_aa_circle(0, 0, 0, tags=("border_oval_3_a", "border_corner_part", "border_parts")) + self.canvas.create_aa_circle(0, 0, 0, tags=("border_oval_3_b", "border_corner_part", "border_parts"), angle=180) + self.canvas.create_aa_circle(0, 0, 0, tags=("border_oval_4_a", "border_corner_part", "border_parts")) + self.canvas.create_aa_circle(0, 0, 0, tags=("border_oval_4_b", "border_corner_part", "border_parts"), angle=180) + self.canvas.tag_lower("border_parts") + + self.canvas.coords("border_oval_1_a", self.corner_radius, self.corner_radius, self.corner_radius) + self.canvas.coords("border_oval_1_b", self.corner_radius, self.corner_radius, self.corner_radius) + self.canvas.coords("border_oval_2_a", self.width - self.corner_radius, self.corner_radius, self.corner_radius) + self.canvas.coords("border_oval_2_b", self.width - self.corner_radius, self.corner_radius, self.corner_radius) + self.canvas.coords("border_oval_3_a", self.width - self.corner_radius, self.height - self.corner_radius, self.corner_radius) + self.canvas.coords("border_oval_3_b", self.width - self.corner_radius, self.height - self.corner_radius, self.corner_radius) + self.canvas.coords("border_oval_4_a", self.corner_radius, self.height - self.corner_radius, self.corner_radius) + self.canvas.coords("border_oval_4_b", self.corner_radius, self.height - self.corner_radius, self.corner_radius) + + if not self.canvas.find_withtag("border_rectangle_1"): + self.canvas.create_rectangle(0, 0, 0, 0, tags=("border_rectangle_1", "border_rectangle_part", "border_parts")) + self.canvas.create_rectangle(0, 0, 0, 0, tags=("border_rectangle_2", "border_rectangle_part", "border_parts")) + self.canvas.tag_lower("border_parts") + + self.canvas.coords("border_rectangle_1", (0, self.corner_radius, self.width - 1, self.height - self.corner_radius - 1)) + self.canvas.coords("border_rectangle_2", (self.corner_radius, 0, self.width - self.corner_radius - 1, self.height - 1)) + + # inner button parts + if self.inner_corner_radius > 0: + + if not self.canvas.find_withtag("inner_oval_1_a"): + self.canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_1_a", "inner_corner_part", "inner_parts")) + self.canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_1_b", "inner_corner_part", "inner_parts"), angle=180) + self.canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_2_a", "inner_corner_part", "inner_parts")) + self.canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_2_b", "inner_corner_part", "inner_parts"), angle=180) + self.canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_3_a", "inner_corner_part", "inner_parts")) + self.canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_3_b", "inner_corner_part", "inner_parts"), angle=180) + self.canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_4_a", "inner_corner_part", "inner_parts")) + self.canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_4_b", "inner_corner_part", "inner_parts"), angle=180) + self.canvas.tag_raise("inner_parts") + + self.canvas.coords("inner_oval_1_a", self.border_width + self.inner_corner_radius, self.border_width + self.inner_corner_radius, + self.inner_corner_radius) + self.canvas.coords("inner_oval_1_b", self.border_width + self.inner_corner_radius, self.border_width + self.inner_corner_radius, + self.inner_corner_radius) + self.canvas.coords("inner_oval_2_a", self.width - self.border_width - self.inner_corner_radius, self.border_width + self.inner_corner_radius, + self.inner_corner_radius) + self.canvas.coords("inner_oval_2_b", self.width - self.border_width - self.inner_corner_radius, self.border_width + self.inner_corner_radius, + self.inner_corner_radius) + self.canvas.coords("inner_oval_3_a", self.width - self.border_width - self.inner_corner_radius, + self.height - self.border_width - self.inner_corner_radius, self.inner_corner_radius) + self.canvas.coords("inner_oval_3_b", self.width - self.border_width - self.inner_corner_radius, + self.height - self.border_width - self.inner_corner_radius, self.inner_corner_radius) + self.canvas.coords("inner_oval_4_a", self.border_width + self.inner_corner_radius, self.height - self.border_width - self.inner_corner_radius, + self.inner_corner_radius) + self.canvas.coords("inner_oval_4_b", self.border_width + self.inner_corner_radius, self.height - self.border_width - self.inner_corner_radius, + self.inner_corner_radius) + else: + self.canvas.delete("inner_corner_part") # delete inner corner parts if not needed + + if not self.canvas.find_withtag("inner_rectangle_1"): + self.canvas.create_rectangle(0, 0, 0, 0, tags=("inner_rectangle_1", "inner_rectangle_part", "inner_parts")) + self.canvas.create_rectangle(0, 0, 0, 0, tags=("inner_rectangle_2", "inner_rectangle_part", "inner_parts")) + self.canvas.tag_raise("inner_parts") + + self.canvas.coords("inner_rectangle_1", (self.border_width + self.inner_corner_radius, + self.border_width, + self.width - self.border_width - self.inner_corner_radius - 1, + self.height - self.border_width - 1)) + self.canvas.coords("inner_rectangle_2", (self.border_width, + self.border_width + self.inner_corner_radius, + self.width - self.border_width - 1, + self.height - self.inner_corner_radius - self.border_width - 1)) + def config(self, *args, **kwargs): self.configure(*args, **kwargs) diff --git a/customtkinter/customtkinter_entry.py b/customtkinter/customtkinter_entry.py index 0800f16..a794278 100644 --- a/customtkinter/customtkinter_entry.py +++ b/customtkinter/customtkinter_entry.py @@ -82,7 +82,7 @@ class CTkEntry(tkinter.Frame): super().configure(width=self.width, height=self.height) self.canvas = tkinter.Canvas(master=self, - highlightthicknes=0, + highlightthickness=0, width=self.width, height=self.height) self.canvas.grid(column=0, row=0, sticky="we") @@ -90,7 +90,7 @@ class CTkEntry(tkinter.Frame): self.entry = tkinter.Entry(master=self, bd=0, width=1, - highlightthicknes=0, + highlightthickness=0, font=self.text_font, **kwargs) self.entry.grid(column=0, row=0, sticky="we", padx=self.corner_radius if self.corner_radius >= 6 else 6) diff --git a/customtkinter/customtkinter_label.py b/customtkinter/customtkinter_label.py index 79a197b..95c97d5 100644 --- a/customtkinter/customtkinter_label.py +++ b/customtkinter/customtkinter_label.py @@ -17,7 +17,7 @@ class CTkLabel(tkinter.Frame): width=120, height=25, text="CTkLabel", - text_font=None, + text_font="default_theme", **kwargs): if master is None: super().__init__(*args) @@ -63,25 +63,16 @@ class CTkLabel(tkinter.Frame): self.corner_radius = self.width / 2 self.text = text - - if text_font is None: - if sys.platform == "darwin": # macOS - self.text_font = ("Avenir", 13) - elif "win" in sys.platform: # Windows - self.text_font = ("Century Gothic", 10) - else: - self.text_font = ("TkDefaultFont", 10) - else: - self.text_font = text_font + self.text_font = (CTkThemeManager.TEXT_FONT_NAME, CTkThemeManager.TEXT_FONT_SIZE) if text_font == "default_theme" else text_font self.canvas = tkinter.Canvas(master=self, - highlightthicknes=0, + highlightthickness=0, width=self.width, height=self.height) self.canvas.place(relx=0, rely=0, anchor=tkinter.NW) self.text_label = tkinter.Label(master=self, - highlightthicknes=0, + highlightthickness=0, bd=0, text=self.text, font=self.text_font, diff --git a/customtkinter/customtkinter_progressbar.py b/customtkinter/customtkinter_progressbar.py index c1ac45c..834ed34 100644 --- a/customtkinter/customtkinter_progressbar.py +++ b/customtkinter/customtkinter_progressbar.py @@ -63,7 +63,7 @@ class CTkProgressBar(tkinter.Frame): self.configure(width=self.width, height=self.height) self.canvas = tkinter.Canvas(master=self, - highlightthicknes=0, + highlightthickness=0, width=self.width, height=self.height) self.canvas.place(x=0, y=0) diff --git a/customtkinter/customtkinter_slider.py b/customtkinter/customtkinter_slider.py index d555b1b..03b0141 100644 --- a/customtkinter/customtkinter_slider.py +++ b/customtkinter/customtkinter_slider.py @@ -52,6 +52,8 @@ class CTkSlider(tkinter.Frame): AppearanceModeTracker.add(self.change_appearance_mode, self) self.appearance_mode = AppearanceModeTracker.get_mode() # 0: "Light" 1: "Dark" + self.configure_basic_grid() + self.bg_color = self.detect_color_of_master() if bg_color is None else bg_color self.border_color = border_color self.fg_color = CTkThemeManager.SLIDER_BG_COLOR if fg_color == "default_theme" else fg_color @@ -82,7 +84,7 @@ class CTkSlider(tkinter.Frame): highlightthickness=0, width=self.width, height=self.height) - self.canvas.place(x=0, y=0) + self.canvas.grid(column=0, row=0, sticky="nswe") self.canvas.bind("", self.on_enter) self.canvas.bind("", self.on_leave) @@ -110,6 +112,10 @@ class CTkSlider(tkinter.Frame): super().destroy() + def configure_basic_grid(self): + self.grid_rowconfigure(0, weight=1) + self.grid_columnconfigure(0, weight=1) + def detect_color_of_master(self): if isinstance(self.master, CTkFrame): return self.master.fg_color @@ -345,10 +351,16 @@ class CTkSlider(tkinter.Frame): return self.output_value def set(self, output_value, from_variable_callback=False): - if output_value > self.to: - output_value = self.to - elif output_value < self.from_: - output_value = self.from_ + if self.from_ < self.to: + if output_value > self.to: + output_value = self.to + elif output_value < self.from_: + output_value = self.from_ + else: + if output_value < self.to: + output_value = self.to + elif output_value > self.from_: + output_value = self.from_ self.output_value = self.round_to_step_size(output_value) self.value = (self.output_value - self.from_) / (self.to - self.from_) diff --git a/customtkinter/customtkinter_theme_manager.py b/customtkinter/customtkinter_theme_manager.py index b203ae4..ca867cc 100644 --- a/customtkinter/customtkinter_theme_manager.py +++ b/customtkinter/customtkinter_theme_manager.py @@ -17,10 +17,21 @@ class CTkThemeManager: FRAME_2_COLOR = None CHECKBOX_LINES_COLOR = None DARKEN_COLOR_FACTOR = None + TEXT_FONT_NAME = None + TEXT_FONT_SIZE = None @classmethod def initialize_color_theme(cls, theme_name): + if sys.platform == "darwin": + cls.TEXT_FONT_NAME = "Avenir" + elif sys.platform.startswith("win"): + cls.TEXT_FONT_NAME = "Segoe UI" + else: + cls.TEXT_FONT_NAME = "TkDefaultFont" + + cls.TEXT_FONT_SIZE = -14 + if theme_name.lower() == "blue": cls.WINDOW_BG_COLOR = ("#ECECEC", "#323232") # macOS standard light and dark window bg colors cls.MAIN_COLOR = ("#64A1D2", "#1C94CF") diff --git a/examples/complex_example.py b/examples/complex_example.py index c704d18..e6d43b2 100644 --- a/examples/complex_example.py +++ b/examples/complex_example.py @@ -4,7 +4,7 @@ import customtkinter import sys customtkinter.set_appearance_mode("Light") # Modes: "System" (standard), "Dark", "Light" -customtkinter.set_default_color_theme("blue") # Themes: "blue" (standard), "green", "dark-blue" +customtkinter.set_default_color_theme("dark-blue") # Themes: "blue" (standard), "green", "dark-blue" class App(customtkinter.CTk): @@ -29,56 +29,71 @@ class App(customtkinter.CTk): # ============ create two CTkFrames ============ self.frame_left = customtkinter.CTkFrame(master=self, - width=200, - height=App.HEIGHT-40, - corner_radius=10) - self.frame_left.place(relx=0.32, rely=0.5, anchor=tkinter.E) + width=180, + corner_radius=0) + self.frame_left.grid(row=0, column=0, sticky="nswe") self.frame_right = customtkinter.CTkFrame(master=self, width=420, height=App.HEIGHT-40, corner_radius=10) - self.frame_right.place(relx=0.365, rely=0.5, anchor=tkinter.W) + self.frame_right.grid(row=0, column=1, sticky="nswe", padx=20, pady=20) + + self.grid_columnconfigure(1, weight=1) + self.rowconfigure(0, weight=1) # ============ frame_left ============ + self.frame_left.grid_rowconfigure(0, minsize=10) + self.frame_left.grid_rowconfigure(5, weight=1) + self.frame_left.grid_rowconfigure(8, minsize=10) + + self.label_1 = customtkinter.CTkLabel(master=self.frame_left, + text="CustomTkinter", + fg_color=None) + self.label_1.grid(row=1, column=0, pady=10, padx=10) + self.button_1 = customtkinter.CTkButton(master=self.frame_left, - text="CTkButton", + text="CTkButton 1", command=self.button_event, border_width=0, corner_radius=8) - self.button_1.place(relx=0.5, y=50, anchor=tkinter.CENTER) + self.button_1.grid(row=2, column=0, pady=10, padx=20) self.button_2 = customtkinter.CTkButton(master=self.frame_left, - text="CTkButton", + text="CTkButton 2", command=self.button_event, border_width=0, corner_radius=8) - self.button_2.place(relx=0.5, y=100, anchor=tkinter.CENTER) + self.button_2.grid(row=3, column=0, pady=10, padx=20) self.button_3 = customtkinter.CTkButton(master=self.frame_left, - text="CTkButton", + text="CTkButton 3", command=self.button_event, border_width=0, corner_radius=8) - self.button_3.place(relx=0.5, y=150, anchor=tkinter.CENTER) + self.button_3.grid(row=4, column=0, pady=10, padx=20) self.check_box_1 = customtkinter.CTkCheckBox(master=self.frame_left, text="CTkCheckBox") - self.check_box_1.place(relx=0.15, rely=0.82, anchor=tkinter.W) + self.check_box_1.grid(row=6, column=0, pady=10, padx=20, sticky="w") self.check_box_2 = customtkinter.CTkCheckBox(master=self.frame_left, text="Dark Mode", command=self.change_mode) - self.check_box_2.place(relx=0.15, rely=0.92, anchor=tkinter.W) + self.check_box_2.grid(row=7, column=0, pady=10, padx=20, sticky="w") # ============ frame_right ============ + self.frame_right.rowconfigure(0, weight=1) + self.frame_right.rowconfigure(3, weight=1) + self.frame_right.columnconfigure(0, weight=1) + self.frame_info = customtkinter.CTkFrame(master=self.frame_right, width=380, height=200, corner_radius=10) - self.frame_info.place(relx=0.5, y=20, anchor=tkinter.N) + self.frame_info.grid(row=0, column=0, columnspan=3, pady=20, padx=20, sticky="wens") # ============ frame_right -> frame_info ============ @@ -103,22 +118,21 @@ class App(customtkinter.CTk): # ============ frame_right <- ============ self.slider_1 = customtkinter.CTkSlider(master=self.frame_right, - width=160, height=16, border_width=5, from_=1, to=0, number_of_steps=3, command=self.progressbar.set) - self.slider_1.place(x=20, rely=0.6, anchor=tkinter.W) - self.slider_1.set(0.3) + self.slider_1.grid(row=1, column=0, columnspan=2, pady=10, padx=20, sticky="we") + self.slider_1.set(0.5) self.slider_2 = customtkinter.CTkSlider(master=self.frame_right, width=160, height=16, border_width=5, command=self.progressbar.set) - self.slider_2.place(x=20, rely=0.7, anchor=tkinter.W) + self.slider_2.grid(row=2, column=0, columnspan=2, pady=10, padx=20, sticky="we") self.slider_2.set(0.7) self.label_info_2 = customtkinter.CTkLabel(master=self.frame_right, @@ -127,7 +141,7 @@ class App(customtkinter.CTk): width=180, height=20, justify=tkinter.CENTER) - self.label_info_2.place(x=310, rely=0.6, anchor=tkinter.CENTER) + self.label_info_2.grid(row=1, column=2, columnspan=1, pady=10, padx=20, sticky="we") self.button_4 = customtkinter.CTkButton(master=self.frame_right, height=25, @@ -135,14 +149,14 @@ class App(customtkinter.CTk): command=self.button_event, border_width=0, corner_radius=8) - self.button_4.place(x=310, rely=0.7, anchor=tkinter.CENTER) + self.button_4.grid(row=2, column=2, columnspan=1, pady=10, padx=20, sticky="we") self.entry = customtkinter.CTkEntry(master=self.frame_right, width=120, height=25, corner_radius=8, placeholder_text="CTkEntry") - self.entry.place(relx=0.33, rely=0.92, anchor=tkinter.CENTER) + self.entry.grid(row=4, column=0, columnspan=2, pady=20, padx=20, sticky="we") self.button_5 = customtkinter.CTkButton(master=self.frame_right, height=25, @@ -150,7 +164,7 @@ class App(customtkinter.CTk): command=self.button_event, border_width=0, corner_radius=8) - self.button_5.place(relx=0.66, rely=0.92, anchor=tkinter.CENTER) + self.button_5.grid(row=4, column=2, columnspan=1, pady=20, padx=20, sticky="we") def button_event(self): print("Button pressed")