finished CTkFont support for button, label, checkbox, created test_font.py

This commit is contained in:
Tom Schimansky
2022-10-21 21:28:31 +02:00
parent 1ae794272b
commit 42fb7f2186
11 changed files with 225 additions and 82 deletions

View File

@@ -63,12 +63,16 @@ class CTkButton(CTkBaseClass):
self._corner_radius = min(self._corner_radius, round(self._current_height/2))
# text, font, image
# text, image
self._image = image
self._image_label: Union[tkinter.Label, None] = None
self._text = text
self._text_label: Union[tkinter.Label, None] = None
self._font = CTkFont() if font == "default_theme" else self._check_font_type_and_values(font)
# font
self._font = CTkFont() if font == "default_theme" else self._check_font_type(font)
if isinstance(self._font, CTkFont):
self._font.add_size_configure_callback(self._update_font)
# callback and hover functionality
self._command = command
@@ -120,6 +124,21 @@ class CTkButton(CTkBaseClass):
height=self._apply_widget_scaling(self._desired_height))
self._draw()
def _update_font(self):
""" pass font to tkinter widgets with applied font scaling and update grid with workaround """
if self._text_label is not None:
self._text_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, rowspan=2, columnspan=2, sticky="nsew")
def destroy(self):
if isinstance(self._font, CTkFont):
self._font.remove_size_configure_callback(self._update_font)
super().destroy()
def _draw(self, no_color_updates=False):
if self._background_corner_colors is not None:
self._draw_engine.draw_background_corners(self._apply_widget_scaling(self._current_width),
@@ -269,7 +288,12 @@ class CTkButton(CTkBaseClass):
self._text_label.configure(text=self._text)
if "font" in kwargs:
self._font = kwargs.pop("font")
if isinstance(self._font, CTkFont):
self._font.remove_size_configure_callback(self._update_font)
self._font = self._check_font_type(kwargs.pop("font"))
if isinstance(self._font, CTkFont):
self._font.add_size_configure_callback(self._update_font)
if self._text_label is not None:
self._text_label.configure(font=self._apply_font_scaling(self._font))

View File

@@ -7,6 +7,7 @@ from ..theme_manager import ThemeManager
from ..settings import Settings
from ..draw_engine import DrawEngine
from .widget_base_class import CTkBaseClass
from ..utility.ctk_font import CTkFont
class CTkCheckBox(CTkBaseClass):
@@ -33,7 +34,7 @@ class CTkCheckBox(CTkBaseClass):
text_color_disabled: Union[str, Tuple[str, str]] = "default_theme",
text: str = "CTkCheckBox",
font: any = "default_theme",
font: Union[tuple, CTkFont] = "default_theme",
textvariable: tkinter.Variable = None,
state: str = tkinter.NORMAL,
hover: bool = True,
@@ -65,7 +66,11 @@ class CTkCheckBox(CTkBaseClass):
self._text_label: Union[tkinter.Label, None] = None
self._text_color = ThemeManager.theme["color"]["text"] if text_color == "default_theme" else text_color
self._text_color_disabled = ThemeManager.theme["color"]["text_disabled"] if text_color_disabled == "default_theme" else text_color_disabled
self._font = (ThemeManager.theme["text"]["font"], ThemeManager.theme["text"]["size"]) if font == "default_theme" else font
# font
self._font = CTkFont() if font == "default_theme" else self._check_font_type(font)
if isinstance(self._font, CTkFont):
self._font.add_size_configure_callback(self._update_font)
# callback and hover functionality
self._command = command
@@ -145,10 +150,23 @@ class CTkCheckBox(CTkBaseClass):
self._bg_canvas.configure(width=self._apply_widget_scaling(self._desired_width),
height=self._apply_widget_scaling(self._desired_height))
def _update_font(self):
""" pass font to tkinter widgets with applied font scaling and update grid with workaround """
if self._text_label is not None:
self._text_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._bg_canvas.grid_forget()
self._bg_canvas.grid(row=0, column=0, sticky="nswe")
def destroy(self):
if self._variable is not None:
self._variable.trace_remove("write", self._variable_callback_name)
if isinstance(self._font, CTkFont):
self._font.remove_size_configure_callback(self._update_font)
super().destroy()
def _draw(self, no_color_updates=False):
@@ -212,9 +230,13 @@ class CTkCheckBox(CTkBaseClass):
self._text_label.configure(text=self._text)
if "font" in kwargs:
self._font = kwargs.pop("font")
if self._text_label is not None:
self._text_label.configure(font=self._apply_font_scaling(self._font))
if isinstance(self._font, CTkFont):
self._font.remove_size_configure_callback(self._update_font)
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 "state" in kwargs:
self._state = kwargs.pop("state")

View File

@@ -5,6 +5,7 @@ from .ctk_canvas import CTkCanvas
from ..theme_manager import ThemeManager
from ..draw_engine import DrawEngine
from .widget_base_class import CTkBaseClass
from ..utility.ctk_font import CTkFont
from customtkinter.utility.utility_functions import pop_from_dict_by_set, check_kwargs_empty
@@ -30,7 +31,7 @@ class CTkLabel(CTkBaseClass):
text_color: Union[str, Tuple[str, str]] = "default_theme",
text: str = "CTkLabel",
font: any = "default_theme",
font: Union[tuple, CTkFont] = "default_theme",
anchor: str = "center", # label anchor: center, n, e, s, w
**kwargs):
@@ -47,7 +48,11 @@ class CTkLabel(CTkBaseClass):
# text
self._anchor = anchor
self._text = text
self._font = (ThemeManager.theme["text"]["font"], ThemeManager.theme["text"]["size"]) if font == "default_theme" else font
# font
self._font = CTkFont() if font == "default_theme" else self._check_font_type(font)
if isinstance(self._font, CTkFont):
self._font.add_size_configure_callback(self._update_font)
# configure grid system (1x1)
self.grid_rowconfigure(0, weight=1)
@@ -97,6 +102,20 @@ class CTkLabel(CTkBaseClass):
height=self._apply_widget_scaling(self._desired_height))
self._draw()
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))
# 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 destroy(self):
if isinstance(self._font, CTkFont):
self._font.remove_size_configure_callback(self._update_font)
super().destroy()
def _draw(self, no_color_updates=False):
requires_recoloring = self._draw_engine.draw_rounded_rect_with_border(self._apply_widget_scaling(self._current_width),
self._apply_widget_scaling(self._current_height),
@@ -133,8 +152,13 @@ class CTkLabel(CTkBaseClass):
self._text_label.configure(text=self._text)
if "font" in kwargs:
self._font = kwargs.pop("font")
self._text_label.configure(font=self._apply_font_scaling(self._font))
if isinstance(self._font, CTkFont):
self._font.remove_size_configure_callback(self._update_font)
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 "fg_color" in kwargs:
self._fg_color = kwargs.pop("fg_color")

View File

@@ -1,3 +1,4 @@
import sys
import tkinter
import tkinter.ttk as ttk
import copy
@@ -118,7 +119,7 @@ class CTkBaseClass(tkinter.Frame):
super().configure(**pop_from_dict_by_set(kwargs, self._valid_tk_frame_attributes)) # configure tkinter.Frame
# if there are still items in the kwargs dict, raise ValueError
self._check_kwargs_empty(kwargs, raise_error=True)
check_kwargs_empty(kwargs, raise_error=True)
if require_redraw:
self._draw()
@@ -139,10 +140,23 @@ class CTkBaseClass(tkinter.Frame):
raise ValueError(f"'{attribute_name}' is not a supported argument. Look at the documentation for supported arguments.")
@staticmethod
def _check_font_type_and_values(font: any):
if not isinstance(font, CTkFont):
raise ValueError(f"\nFor consistency, Customtkinter requires the font argument {font} to be an instance of CTkFont.\n" +
f"\nUsage example: font=customtkinter.CTkFont(family='name', size='size in px')\n(other arguments in the documentation)\n")
def _check_font_type(font: any):
if isinstance(font, CTkFont):
return font
elif type(font) == tuple and len(font) == 1:
sys.stderr.write(f"Warning: font {font} given without size, will be extended with default text size of current theme\n")
return font[0], ThemeManager.theme["text"]["size"]
elif type(font) == tuple and 2 <= len(font) <= 3:
return font
else:
raise ValueError(f"Wrong font type {type(font)}\n" +
f"For consistency, Customtkinter requires the font argument to be a tuple of len 2 or 3 or an instance of CTkFont.\n" +
f"\nUsage example:\n" +
f"font=customtkinter.CTkFont(family='<name>', size=<size in px>)\n" +
f"font=('<name>', <size in px>)\n")
def _update_dimensions_event(self, event):
# only redraw if dimensions changed (for performance), independent of scaling
@@ -219,15 +233,22 @@ class CTkBaseClass(tkinter.Frame):
else:
return value
def _apply_font_scaling(self, font: Union[tuple, CTkFont]) -> Union[tuple, CTkFont]:
def _apply_font_scaling(self, font: Union[Tuple, CTkFont]) -> tuple:
""" Takes CTkFont object and returns tuple font with scaled size, has to be called again for every change of font object """
if type(font) == tuple:
font_list = list(font)
if len(font_list) >= 2 and type(font_list[1]) == int:
font_list[1] = int(font_list[1] * self._widget_scaling)
return tuple(font_list)
if len(font) == 1:
return font
elif len(font) == 2:
return font[0], round(font[1] * self._widget_scaling)
elif len(font) == 3:
return font[0], round(font[1] * self._widget_scaling), font[2]
else:
raise ValueError(f"Can not scale font {font}. font needs to be tuple of len 1, 2 or 3")
elif isinstance(font, CTkFont):
return font
return font.create_scaled_tuple(self._widget_scaling)
else:
raise ValueError(f"Can not scale font '{font}' of type {type(font)}. font needs to be tuple or instance of CTkFont")
def _apply_argument_scaling(self, kwargs: dict) -> dict:
scaled_kwargs = copy.copy(kwargs)