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:
- 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

View File

@ -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,32 +74,34 @@ 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,
self._label = tkinter.Label(master=self,
highlightthickness=0,
padx=0,
pady=0,
borderwidth=1,
borderwidth=0,
anchor=self._anchor,
compound=self._compound,
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.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,
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,
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),
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),
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,
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,
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()

View File

@ -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)

View File

@ -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()

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB