diff --git a/CHANGELOG.md b/CHANGELOG.md index 40557c8..a2e50bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,8 +6,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ToDo: - complete other theme files - - auto-scaling of images - - image tuple for light/dark mode - change font attribute in wiki - add new button attributes to wiki - cursor configuring diff --git a/customtkinter/windows/widgets/ctk_label.py b/customtkinter/windows/widgets/ctk_label.py index c4e92bd..15ee915 100644 --- a/customtkinter/windows/widgets/ctk_label.py +++ b/customtkinter/windows/widgets/ctk_label.py @@ -6,6 +6,7 @@ from .theme.theme_manager import ThemeManager from .core_rendering.draw_engine import DrawEngine from .core_widget_classes.widget_base_class import CTkBaseClass from .font.ctk_font import CTkFont +from .image.ctk_image import CTkImage from customtkinter.utility.utility_functions import pop_from_dict_by_set, check_kwargs_empty @@ -17,7 +18,7 @@ class CTkLabel(CTkBaseClass): """ # attributes that are passed to and managed by the tkinter entry only: - _valid_tk_label_attributes = {"compound", "cursor", "image", "justify", "padx", "pady", + _valid_tk_label_attributes = {"cursor", "justify", "padx", "pady", "textvariable", "state", "takefocus", "underline", "wraplength"} def __init__(self, @@ -32,6 +33,8 @@ class CTkLabel(CTkBaseClass): text: str = "CTkLabel", font: Union[tuple, CTkFont] = "default_theme", + image: Union[tkinter.PhotoImage, CTkImage] = None, + compound: str = "center", anchor: str = "center", # label anchor: center, n, e, s, w **kwargs): @@ -49,6 +52,12 @@ class CTkLabel(CTkBaseClass): self._anchor = anchor self._text = text + # image + self._image = image + self._compound = compound + if isinstance(self._image, CTkImage): + self._image.add_configure_callback(self._update_image) + # font self._font = CTkFont() if font == "default_theme" else self._check_font_type(font) if isinstance(self._font, CTkFont): @@ -65,33 +74,35 @@ class CTkLabel(CTkBaseClass): self._canvas.grid(row=0, column=0, sticky="nswe") self._draw_engine = DrawEngine(self._canvas) - self._text_label = tkinter.Label(master=self, - highlightthickness=0, - padx=0, - pady=0, - borderwidth=1, - anchor=self._anchor, - text=self._text, - font=self._apply_font_scaling(self._font)) - self._text_label.configure(**pop_from_dict_by_set(kwargs, self._valid_tk_label_attributes)) + self._label = tkinter.Label(master=self, + highlightthickness=0, + padx=0, + pady=0, + borderwidth=0, + anchor=self._anchor, + compound=self._compound, + text=self._text, + font=self._apply_font_scaling(self._font)) + self._label.configure(**pop_from_dict_by_set(kwargs, self._valid_tk_label_attributes)) text_label_grid_sticky = self._anchor if self._anchor != "center" else "" - self._text_label.grid(row=0, column=0, sticky=text_label_grid_sticky, - padx=self._apply_widget_scaling(min(self._corner_radius, round(self._current_height/2)))) + self._label.grid(row=0, column=0, sticky=text_label_grid_sticky, + padx=self._apply_widget_scaling(min(self._corner_radius, round(self._current_height/2)))) check_kwargs_empty(kwargs, raise_error=True) + self._update_image() self._draw() def _set_scaling(self, *args, **kwargs): super()._set_scaling(*args, **kwargs) self._canvas.configure(width=self._apply_widget_scaling(self._desired_width), height=self._apply_widget_scaling(self._desired_height)) - self._text_label.configure(font=self._apply_font_scaling(self._font)) + self._label.configure(font=self._apply_font_scaling(self._font)) text_label_grid_sticky = self._anchor if self._anchor != "center" else "" - self._text_label.grid(row=0, column=0, sticky=text_label_grid_sticky, - padx=self._apply_widget_scaling(min(self._corner_radius, round(self._current_height/2)))) + self._label.grid(row=0, column=0, sticky=text_label_grid_sticky, + padx=self._apply_widget_scaling(min(self._corner_radius, round(self._current_height/2)))) self._draw(no_color_updates=True) @@ -104,13 +115,20 @@ class CTkLabel(CTkBaseClass): def _update_font(self): """ pass font to tkinter widgets with applied font scaling and update grid with workaround """ - self._text_label.configure(font=self._apply_font_scaling(self._font)) + self._label.configure(font=self._apply_font_scaling(self._font)) # Workaround to force grid to be resized when text changes size. # Otherwise grid will lag and only resizes if other mouse action occurs. self._canvas.grid_forget() self._canvas.grid(row=0, column=0, sticky="nswe") + def _update_image(self): + if isinstance(self._image, CTkImage): + self._label.configure(image=self._image.create_scaled_photo_image(self._get_widget_scaling(), + self._get_appearance_mode())) + elif self._image is not None: + self._label.configure(image=self._image) + def destroy(self): if isinstance(self._font, CTkFont): self._font.remove_size_configure_callback(self._update_font) @@ -130,28 +148,35 @@ class CTkLabel(CTkBaseClass): fill=self._apply_appearance_mode(self._fg_color), outline=self._apply_appearance_mode(self._fg_color)) - self._text_label.configure(fg=self._apply_appearance_mode(self._text_color), - bg=self._apply_appearance_mode(self._fg_color)) + self._label.configure(fg=self._apply_appearance_mode(self._text_color), + bg=self._apply_appearance_mode(self._fg_color)) else: self._canvas.itemconfig("inner_parts", fill=self._apply_appearance_mode(self._bg_color), outline=self._apply_appearance_mode(self._bg_color)) - self._text_label.configure(fg=self._apply_appearance_mode(self._text_color), - bg=self._apply_appearance_mode(self._bg_color)) + self._label.configure(fg=self._apply_appearance_mode(self._text_color), + bg=self._apply_appearance_mode(self._bg_color)) self._canvas.configure(bg=self._apply_appearance_mode(self._bg_color)) + if self._image is not None: + self._update_image() + def configure(self, require_redraw=False, **kwargs): if "anchor" in kwargs: self._anchor = kwargs.pop("anchor") text_label_grid_sticky = self._anchor if self._anchor != "center" else "" - self._text_label.grid(row=0, column=0, sticky=text_label_grid_sticky, - padx=self._apply_widget_scaling(min(self._corner_radius, round(self._current_height/2)))) + self._label.grid(row=0, column=0, sticky=text_label_grid_sticky, + padx=self._apply_widget_scaling(min(self._corner_radius, round(self._current_height/2)))) + + if "compound" in kwargs: + self._compound = kwargs.pop("compound") + self._label.configure(compound=self._compound) if "text" in kwargs: self._text = kwargs.pop("text") - self._text_label.configure(text=self._text) + self._label.configure(text=self._text) if "font" in kwargs: if isinstance(self._font, CTkFont): @@ -159,9 +184,16 @@ class CTkLabel(CTkBaseClass): self._font = self._check_font_type(kwargs.pop("font")) if isinstance(self._font, CTkFont): self._font.add_size_configure_callback(self._update_font) - self._update_font() + if "image" in kwargs: + if isinstance(self._image, CTkImage): + self._image.remove_configure_callback(self._update_image) + self._image = kwargs.pop("image") + if isinstance(self._image, CTkImage): + self._image.add_configure_callback(self._update_image) + self._update_image() + if "fg_color" in kwargs: self._fg_color = kwargs.pop("fg_color") require_redraw = True @@ -173,11 +205,11 @@ class CTkLabel(CTkBaseClass): if "corner_radius" in kwargs: self._corner_radius = kwargs.pop("corner_radius") text_label_grid_sticky = self._anchor if self._anchor != "center" else "" - self._text_label.grid(row=0, column=0, sticky=text_label_grid_sticky, - padx=self._apply_widget_scaling(min(self._corner_radius, round(self._current_height/2)))) + self._label.grid(row=0, column=0, sticky=text_label_grid_sticky, + padx=self._apply_widget_scaling(min(self._corner_radius, round(self._current_height/2)))) require_redraw = True - self._text_label.configure(**pop_from_dict_by_set(kwargs, self._valid_tk_label_attributes)) # configure tkinter.Label + self._label.configure(**pop_from_dict_by_set(kwargs, self._valid_tk_label_attributes)) # configure tkinter.Label super().configure(require_redraw=require_redraw, **kwargs) # configure CTkBaseClass def cget(self, attribute_name: str) -> any: @@ -193,31 +225,35 @@ class CTkLabel(CTkBaseClass): return self._text elif attribute_name == "font": return self._font + elif attribute_name == "image": + return self._image + elif attribute_name == "compound": + return self._compound elif attribute_name == "anchor": return self._anchor elif attribute_name in self._valid_tk_label_attributes: - return self._text_label.cget(attribute_name) # cget of tkinter.Label + return self._label.cget(attribute_name) # cget of tkinter.Label else: return super().cget(attribute_name) # cget of CTkBaseClass def bind(self, sequence: str = None, command: Callable = None, add: str = None) -> str: """ called on the tkinter.Label and tkinter.Canvas """ canvas_bind_return = self._canvas.bind(sequence, command, add) - label_bind_return = self._text_label.bind(sequence, command, add) + label_bind_return = self._label.bind(sequence, command, add) return canvas_bind_return + " + " + label_bind_return def unbind(self, sequence: str, funcid: str = None): """ called on the tkinter.Label and tkinter.Canvas """ canvas_bind_return, label_bind_return = funcid.split(" + ") self._canvas.unbind(sequence, canvas_bind_return) - self._text_label.unbind(sequence, label_bind_return) + self._label.unbind(sequence, label_bind_return) def focus(self): - return self._text_label.focus() + return self._label.focus() def focus_set(self): - return self._text_label.focus_set() + return self._label.focus_set() def focus_force(self): - return self._text_label.focus_force() + return self._label.focus_force() diff --git a/customtkinter/windows/widgets/image/ctk_image.py b/customtkinter/windows/widgets/image/ctk_image.py index 82bdc51..e197b56 100644 --- a/customtkinter/windows/widgets/image/ctk_image.py +++ b/customtkinter/windows/widgets/image/ctk_image.py @@ -27,7 +27,6 @@ class CTkImage: self._check_pil_import() self._light_image = light_image - print(self._light_image) self._dark_image = dark_image self._check_images() self._size = size @@ -109,7 +108,6 @@ class CTkImage: def create_scaled_photo_image(self, widget_scaling: float, appearance_mode: str) -> ImageTk.PhotoImage: scaled_size = self._get_scaled_size(widget_scaling) - print(scaled_size) if appearance_mode == "light" and self._light_image is not None: return self._get_scaled_light_photo_image(scaled_size) diff --git a/examples/example_button_images.py b/examples/example_button_images.py index ac6fb7a..89c0709 100644 --- a/examples/example_button_images.py +++ b/examples/example_button_images.py @@ -2,21 +2,34 @@ import customtkinter from PIL import Image, ImageTk import os -PATH = os.path.dirname(os.path.realpath(__file__)) - +# load images +file_path = os.path.dirname(os.path.realpath(__file__)) +image_1 = customtkinter.CTkImage(light_image=Image.open(file_path + "/test_images/add_folder_dark.png"), + dark_image=Image.open(file_path + "/test_images/add_folder_light.png"), + size=(30, 30)) +image_1.configure(dark_image=Image.open(file_path + "/test_images/add_folder_light.png")) +image_2 = customtkinter.CTkImage(light_image=Image.open(file_path + "/test_images/bg_gradient.jpg"), + size=(30, 50)) app = customtkinter.CTk() -switch_1 = customtkinter.CTkSwitch(app, text="darkmode", command=lambda: customtkinter.set_appearance_mode("dark" if switch_1.get() == 1 else "light")) -switch_1.pack(padx=20, pady=20) +mode_switch = customtkinter.CTkSwitch(app, text="darkmode", + command=lambda: customtkinter.set_appearance_mode("dark" if mode_switch.get() == 1 else "light")) +mode_switch.pack(padx=20, pady=20) -image_1 = customtkinter.CTkImage(light_image=Image.open(PATH + "/test_images/add_folder_dark.png"), - dark_image=Image.open(PATH + "/test_images/add_folder_light.png"), - size=(30, 50)) -image_1.configure(dark_image=Image.open(PATH + "/test_images/add_folder_light.png")) +scaling_button = customtkinter.CTkSegmentedButton(app, values=[0.8, 0.9, 1.0, 1.1, 1.2, 1.5, 2.0], + command=lambda v: customtkinter.set_widget_scaling(v)) +scaling_button.pack(padx=20, pady=20) button_1 = customtkinter.CTkButton(app, image=image_1) button_1.pack(padx=20, pady=20) +label_1 = customtkinter.CTkLabel(app, text="", image=image_2, compound="right", fg_color="green", width=0) +label_1.pack(padx=20, pady=20) +label_1.configure(image=image_1) +label_2 = customtkinter.CTkLabel(app, image=ImageTk.PhotoImage(Image.open(file_path + "/test_images/bg_gradient.jpg").resize((100, 100))), + text="", compound="right", fg_color="green", width=0) +label_2.pack(padx=20, pady=20) + app.mainloop() diff --git a/examples/test_images/add_folder_dark.png b/examples/test_images/add_folder_dark.png new file mode 100644 index 0000000..dcf4f2d Binary files /dev/null and b/examples/test_images/add_folder_dark.png differ diff --git a/examples/test_images/add_folder_light.png b/examples/test_images/add_folder_light.png new file mode 100644 index 0000000..feff512 Binary files /dev/null and b/examples/test_images/add_folder_light.png differ