added CTkImage, compound support for label

This commit is contained in:
Tom Schimansky 2022-11-03 13:48:31 +01:00
parent 7374e7a3bc
commit 62b330ddba
6 changed files with 90 additions and 45 deletions

View File

@ -6,8 +6,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
ToDo: ToDo:
- complete other theme files - complete other theme files
- auto-scaling of images
- image tuple for light/dark mode
- change font attribute in wiki - change font attribute in wiki
- add new button attributes to wiki - add new button attributes to wiki
- cursor configuring - cursor configuring

View File

@ -6,6 +6,7 @@ from .theme.theme_manager import ThemeManager
from .core_rendering.draw_engine import DrawEngine from .core_rendering.draw_engine import DrawEngine
from .core_widget_classes.widget_base_class import CTkBaseClass from .core_widget_classes.widget_base_class import CTkBaseClass
from .font.ctk_font import CTkFont 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 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: # 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"} "textvariable", "state", "takefocus", "underline", "wraplength"}
def __init__(self, def __init__(self,
@ -32,6 +33,8 @@ class CTkLabel(CTkBaseClass):
text: str = "CTkLabel", text: str = "CTkLabel",
font: Union[tuple, CTkFont] = "default_theme", 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 anchor: str = "center", # label anchor: center, n, e, s, w
**kwargs): **kwargs):
@ -49,6 +52,12 @@ class CTkLabel(CTkBaseClass):
self._anchor = anchor self._anchor = anchor
self._text = text self._text = text
# image
self._image = image
self._compound = compound
if isinstance(self._image, CTkImage):
self._image.add_configure_callback(self._update_image)
# font # font
self._font = CTkFont() if font == "default_theme" else self._check_font_type(font) self._font = CTkFont() if font == "default_theme" else self._check_font_type(font)
if isinstance(self._font, CTkFont): if isinstance(self._font, CTkFont):
@ -65,33 +74,35 @@ class CTkLabel(CTkBaseClass):
self._canvas.grid(row=0, column=0, sticky="nswe") self._canvas.grid(row=0, column=0, sticky="nswe")
self._draw_engine = DrawEngine(self._canvas) self._draw_engine = DrawEngine(self._canvas)
self._text_label = tkinter.Label(master=self, self._label = tkinter.Label(master=self,
highlightthickness=0, highlightthickness=0,
padx=0, padx=0,
pady=0, pady=0,
borderwidth=1, borderwidth=0,
anchor=self._anchor, anchor=self._anchor,
text=self._text, compound=self._compound,
font=self._apply_font_scaling(self._font)) text=self._text,
self._text_label.configure(**pop_from_dict_by_set(kwargs, self._valid_tk_label_attributes)) 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 "" text_label_grid_sticky = self._anchor if self._anchor != "center" else ""
self._text_label.grid(row=0, column=0, sticky=text_label_grid_sticky, 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)))) padx=self._apply_widget_scaling(min(self._corner_radius, round(self._current_height/2))))
check_kwargs_empty(kwargs, raise_error=True) check_kwargs_empty(kwargs, raise_error=True)
self._update_image()
self._draw() self._draw()
def _set_scaling(self, *args, **kwargs): def _set_scaling(self, *args, **kwargs):
super()._set_scaling(*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._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 "" text_label_grid_sticky = self._anchor if self._anchor != "center" else ""
self._text_label.grid(row=0, column=0, sticky=text_label_grid_sticky, 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)))) padx=self._apply_widget_scaling(min(self._corner_radius, round(self._current_height/2))))
self._draw(no_color_updates=True) self._draw(no_color_updates=True)
@ -104,13 +115,20 @@ class CTkLabel(CTkBaseClass):
def _update_font(self): def _update_font(self):
""" pass font to tkinter widgets with applied font scaling and update grid with workaround """ """ 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. # Workaround to force grid to be resized when text changes size.
# Otherwise grid will lag and only resizes if other mouse action occurs. # Otherwise grid will lag and only resizes if other mouse action occurs.
self._canvas.grid_forget() self._canvas.grid_forget()
self._canvas.grid(row=0, column=0, sticky="nswe") 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): def destroy(self):
if isinstance(self._font, CTkFont): if isinstance(self._font, CTkFont):
self._font.remove_size_configure_callback(self._update_font) self._font.remove_size_configure_callback(self._update_font)
@ -130,28 +148,35 @@ class CTkLabel(CTkBaseClass):
fill=self._apply_appearance_mode(self._fg_color), fill=self._apply_appearance_mode(self._fg_color),
outline=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), self._label.configure(fg=self._apply_appearance_mode(self._text_color),
bg=self._apply_appearance_mode(self._fg_color)) bg=self._apply_appearance_mode(self._fg_color))
else: else:
self._canvas.itemconfig("inner_parts", self._canvas.itemconfig("inner_parts",
fill=self._apply_appearance_mode(self._bg_color), fill=self._apply_appearance_mode(self._bg_color),
outline=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), self._label.configure(fg=self._apply_appearance_mode(self._text_color),
bg=self._apply_appearance_mode(self._bg_color)) bg=self._apply_appearance_mode(self._bg_color))
self._canvas.configure(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): def configure(self, require_redraw=False, **kwargs):
if "anchor" in kwargs: if "anchor" in kwargs:
self._anchor = kwargs.pop("anchor") self._anchor = kwargs.pop("anchor")
text_label_grid_sticky = self._anchor if self._anchor != "center" else "" text_label_grid_sticky = self._anchor if self._anchor != "center" else ""
self._text_label.grid(row=0, column=0, sticky=text_label_grid_sticky, 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)))) 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: if "text" in kwargs:
self._text = kwargs.pop("text") self._text = kwargs.pop("text")
self._text_label.configure(text=self._text) self._label.configure(text=self._text)
if "font" in kwargs: if "font" in kwargs:
if isinstance(self._font, CTkFont): if isinstance(self._font, CTkFont):
@ -159,9 +184,16 @@ class CTkLabel(CTkBaseClass):
self._font = self._check_font_type(kwargs.pop("font")) self._font = self._check_font_type(kwargs.pop("font"))
if isinstance(self._font, CTkFont): if isinstance(self._font, CTkFont):
self._font.add_size_configure_callback(self._update_font) self._font.add_size_configure_callback(self._update_font)
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: if "fg_color" in kwargs:
self._fg_color = kwargs.pop("fg_color") self._fg_color = kwargs.pop("fg_color")
require_redraw = True require_redraw = True
@ -173,11 +205,11 @@ class CTkLabel(CTkBaseClass):
if "corner_radius" in kwargs: if "corner_radius" in kwargs:
self._corner_radius = kwargs.pop("corner_radius") self._corner_radius = kwargs.pop("corner_radius")
text_label_grid_sticky = self._anchor if self._anchor != "center" else "" text_label_grid_sticky = self._anchor if self._anchor != "center" else ""
self._text_label.grid(row=0, column=0, sticky=text_label_grid_sticky, 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)))) padx=self._apply_widget_scaling(min(self._corner_radius, round(self._current_height/2))))
require_redraw = True 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 super().configure(require_redraw=require_redraw, **kwargs) # configure CTkBaseClass
def cget(self, attribute_name: str) -> any: def cget(self, attribute_name: str) -> any:
@ -193,31 +225,35 @@ class CTkLabel(CTkBaseClass):
return self._text return self._text
elif attribute_name == "font": elif attribute_name == "font":
return self._font return self._font
elif attribute_name == "image":
return self._image
elif attribute_name == "compound":
return self._compound
elif attribute_name == "anchor": elif attribute_name == "anchor":
return self._anchor return self._anchor
elif attribute_name in self._valid_tk_label_attributes: 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: else:
return super().cget(attribute_name) # cget of CTkBaseClass return super().cget(attribute_name) # cget of CTkBaseClass
def bind(self, sequence: str = None, command: Callable = None, add: str = None) -> str: def bind(self, sequence: str = None, command: Callable = None, add: str = None) -> str:
""" called on the tkinter.Label and tkinter.Canvas """ """ called on the tkinter.Label and tkinter.Canvas """
canvas_bind_return = self._canvas.bind(sequence, command, add) 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 return canvas_bind_return + " + " + label_bind_return
def unbind(self, sequence: str, funcid: str = None): def unbind(self, sequence: str, funcid: str = None):
""" called on the tkinter.Label and tkinter.Canvas """ """ called on the tkinter.Label and tkinter.Canvas """
canvas_bind_return, label_bind_return = funcid.split(" + ") canvas_bind_return, label_bind_return = funcid.split(" + ")
self._canvas.unbind(sequence, canvas_bind_return) 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): def focus(self):
return self._text_label.focus() return self._label.focus()
def focus_set(self): def focus_set(self):
return self._text_label.focus_set() return self._label.focus_set()
def focus_force(self): def focus_force(self):
return self._text_label.focus_force() return self._label.focus_force()

View File

@ -27,7 +27,6 @@ class CTkImage:
self._check_pil_import() self._check_pil_import()
self._light_image = light_image self._light_image = light_image
print(self._light_image)
self._dark_image = dark_image self._dark_image = dark_image
self._check_images() self._check_images()
self._size = size self._size = size
@ -109,7 +108,6 @@ class CTkImage:
def create_scaled_photo_image(self, widget_scaling: float, appearance_mode: str) -> ImageTk.PhotoImage: def create_scaled_photo_image(self, widget_scaling: float, appearance_mode: str) -> ImageTk.PhotoImage:
scaled_size = self._get_scaled_size(widget_scaling) scaled_size = self._get_scaled_size(widget_scaling)
print(scaled_size)
if appearance_mode == "light" and self._light_image is not None: if appearance_mode == "light" and self._light_image is not None:
return self._get_scaled_light_photo_image(scaled_size) return self._get_scaled_light_photo_image(scaled_size)

View File

@ -2,21 +2,34 @@ import customtkinter
from PIL import Image, ImageTk from PIL import Image, ImageTk
import os 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() app = customtkinter.CTk()
switch_1 = customtkinter.CTkSwitch(app, text="darkmode", command=lambda: customtkinter.set_appearance_mode("dark" if switch_1.get() == 1 else "light")) mode_switch = customtkinter.CTkSwitch(app, text="darkmode",
switch_1.pack(padx=20, pady=20) 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"), scaling_button = customtkinter.CTkSegmentedButton(app, values=[0.8, 0.9, 1.0, 1.1, 1.2, 1.5, 2.0],
dark_image=Image.open(PATH + "/test_images/add_folder_light.png"), command=lambda v: customtkinter.set_widget_scaling(v))
size=(30, 50)) scaling_button.pack(padx=20, pady=20)
image_1.configure(dark_image=Image.open(PATH + "/test_images/add_folder_light.png"))
button_1 = customtkinter.CTkButton(app, image=image_1) button_1 = customtkinter.CTkButton(app, image=image_1)
button_1.pack(padx=20, pady=20) 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() app.mainloop()

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB