Umstellung auf Customtkinter.

This commit is contained in:
2024-07-15 09:40:40 +02:00
parent e0d1fc976a
commit 0c4926f0d5
123 changed files with 11824 additions and 46 deletions

View File

@@ -0,0 +1,24 @@
import os
import sys
from .ctk_font import CTkFont
from .font_manager import FontManager
# import DrawEngine to set preferred_drawing_method if loading shapes font fails
from ..core_rendering import DrawEngine
FontManager.init_font_manager()
# load Roboto fonts (used on Windows/Linux)
customtkinter_directory = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
FontManager.load_font(os.path.join(customtkinter_directory, "assets", "fonts", "Roboto", "Roboto-Regular.ttf"))
FontManager.load_font(os.path.join(customtkinter_directory, "assets", "fonts", "Roboto", "Roboto-Medium.ttf"))
# load font necessary for rendering the widgets (used on Windows/Linux)
if FontManager.load_font(os.path.join(customtkinter_directory, "assets", "fonts", "CustomTkinter_shapes_font.otf")) is False:
# change draw method if font loading failed
if DrawEngine.preferred_drawing_method == "font_shapes":
sys.stderr.write("customtkinter.windows.widgets.font warning: " +
"Preferred drawing method 'font_shapes' can not be used because the font file could not be loaded.\n" +
"Using 'circle_shapes' instead. The rendering quality will be bad!\n")
DrawEngine.preferred_drawing_method = "circle_shapes"

View File

@@ -0,0 +1,94 @@
from tkinter.font import Font
import copy
from typing import List, Callable, Tuple, Optional
try:
from typing import Literal
except ImportError:
from typing_extensions import Literal
from ..theme import ThemeManager
class CTkFont(Font):
"""
Font object with size in pixel, independent of scaling.
To get scaled tuple representation use create_scaled_tuple() method.
family The font family name as a string.
size The font height as an integer in pixel.
weight 'bold' for boldface, 'normal' for regular weight.
slant 'italic' for italic, 'roman' for unslanted.
underline 1 for underlined text, 0 for normal.
overstrike 1 for overstruck text, 0 for normal.
Tkinter Font: https://anzeljg.github.io/rin2/book2/2405/docs/tkinter/fonts.html
"""
def __init__(self,
family: Optional[str] = None,
size: Optional[int] = None,
weight: Literal["normal", "bold"] = None,
slant: Literal["italic", "roman"] = "roman",
underline: bool = False,
overstrike: bool = False):
self._size_configure_callback_list: List[Callable] = []
self._size = ThemeManager.theme["CTkFont"]["size"] if size is None else size
super().__init__(family=ThemeManager.theme["CTkFont"]["family"] if family is None else family,
size=-abs(self._size),
weight=ThemeManager.theme["CTkFont"]["weight"] if weight is None else weight,
slant=slant,
underline=underline,
overstrike=overstrike)
self._family = super().cget("family")
self._tuple_style_string = f"{super().cget('weight')} {slant} {'underline' if underline else ''} {'overstrike' if overstrike else ''}"
def add_size_configure_callback(self, callback: Callable):
""" add function, that gets called when font got configured """
self._size_configure_callback_list.append(callback)
def remove_size_configure_callback(self, callback: Callable):
""" remove function, that gets called when font got configured """
try:
self._size_configure_callback_list.remove(callback)
except ValueError:
pass
def create_scaled_tuple(self, font_scaling: float) -> Tuple[str, int, str]:
""" return scaled tuple representation of font in the form (family: str, size: int, style: str)"""
return self._family, round(-abs(self._size) * font_scaling), self._tuple_style_string
def config(self, *args, **kwargs):
raise AttributeError("'config' is not implemented for CTk widgets. For consistency, always use 'configure' instead.")
def configure(self, **kwargs):
if "size" in kwargs:
self._size = kwargs.pop("size")
super().configure(size=-abs(self._size))
if "family" in kwargs:
super().configure(family=kwargs.pop("family"))
self._family = super().cget("family")
super().configure(**kwargs)
# update style string for create_scaled_tuple() method
self._tuple_style_string = f"{super().cget('weight')} {super().cget('slant')} {'underline' if super().cget('underline') else ''} {'overstrike' if super().cget('overstrike') else ''}"
# call all functions registered with add_size_configure_callback()
for callback in self._size_configure_callback_list:
callback()
def cget(self, attribute_name: str) -> any:
if attribute_name == "size":
return self._size
if attribute_name == "family":
return self._family
else:
return super().cget(attribute_name)
def copy(self) -> "CTkFont":
return copy.deepcopy(self)

View File

@@ -0,0 +1,66 @@
import sys
import os
import shutil
from typing import Union
class FontManager:
linux_font_path = "~/.fonts/"
@classmethod
def init_font_manager(cls):
# Linux
if sys.platform.startswith("linux"):
try:
if not os.path.isdir(os.path.expanduser(cls.linux_font_path)):
os.mkdir(os.path.expanduser(cls.linux_font_path))
return True
except Exception as err:
sys.stderr.write("FontManager error: " + str(err) + "\n")
return False
# other platforms
else:
return True
@classmethod
def windows_load_font(cls, font_path: Union[str, bytes], private: bool = True, enumerable: bool = False) -> bool:
""" Function taken from: https://stackoverflow.com/questions/11993290/truly-custom-font-in-tkinter/30631309#30631309 """
from ctypes import windll, byref, create_unicode_buffer, create_string_buffer
FR_PRIVATE = 0x10
FR_NOT_ENUM = 0x20
if isinstance(font_path, bytes):
path_buffer = create_string_buffer(font_path)
add_font_resource_ex = windll.gdi32.AddFontResourceExA
elif isinstance(font_path, str):
path_buffer = create_unicode_buffer(font_path)
add_font_resource_ex = windll.gdi32.AddFontResourceExW
else:
raise TypeError('font_path must be of type bytes or str')
flags = (FR_PRIVATE if private else 0) | (FR_NOT_ENUM if not enumerable else 0)
num_fonts_added = add_font_resource_ex(byref(path_buffer), flags, 0)
return bool(min(num_fonts_added, 1))
@classmethod
def load_font(cls, font_path: str) -> bool:
# Windows
if sys.platform.startswith("win"):
return cls.windows_load_font(font_path, private=True, enumerable=False)
# Linux
elif sys.platform.startswith("linux"):
try:
shutil.copy(font_path, os.path.expanduser(cls.linux_font_path))
return True
except Exception as err:
sys.stderr.write("FontManager error: " + str(err) + "\n")
return False
# macOS and others
else:
return False