mirror of
https://github.com/TomSchimansky/CustomTkinter.git
synced 2023-08-10 21:13:13 +03:00
architecture fixes
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import tkinter
|
||||
from typing import Union, Tuple
|
||||
from abc import ABC, abstractmethod
|
||||
import copy
|
||||
import re
|
||||
try:
|
||||
@@ -9,69 +9,109 @@ except ImportError:
|
||||
|
||||
from .scaling_tracker import ScalingTracker
|
||||
from ..font.ctk_font import CTkFont
|
||||
from ..image.ctk_image import CTkImage
|
||||
|
||||
|
||||
class CTkScalingBaseClass(ABC):
|
||||
class CTkScalingBaseClass():
|
||||
"""
|
||||
Super-class that manages the scaling values and callbacks.
|
||||
Works for widgets and windows, type must be set in init method with
|
||||
scaling_type attribute. Methods:
|
||||
|
||||
- _set_scaling() abstractmethod, gets called when scaling changes, must be overridden
|
||||
- destroy() must be called when sub-class is destroyed
|
||||
- _apply_widget_scaling()
|
||||
- _reverse_widget_scaling()
|
||||
- _apply_window_scaling()
|
||||
- _reverse_window_scaling()
|
||||
- _apply_font_scaling()
|
||||
- _apply_argument_scaling()
|
||||
- _apply_geometry_scaling()
|
||||
- _reverse_geometry_scaling()
|
||||
- _parse_geometry_string()
|
||||
|
||||
"""
|
||||
def __init__(self, scaling_type: Literal["widget", "window"] = "widget"):
|
||||
self._scaling_type = scaling_type
|
||||
self.__scaling_type = scaling_type
|
||||
|
||||
if self._scaling_type == "widget":
|
||||
if self.__scaling_type == "widget":
|
||||
ScalingTracker.add_widget(self._set_scaling, self) # add callback for automatic scaling changes
|
||||
self._widget_scaling = ScalingTracker.get_widget_scaling(self)
|
||||
elif self._scaling_type == "window":
|
||||
self.__widget_scaling = ScalingTracker.get_widget_scaling(self)
|
||||
elif self.__scaling_type == "window":
|
||||
ScalingTracker.activate_high_dpi_awareness() # make process DPI aware
|
||||
ScalingTracker.add_window(self._set_scaling, self) # add callback for automatic scaling changes
|
||||
self._window_scaling = ScalingTracker.get_window_scaling(self)
|
||||
self.__window_scaling = ScalingTracker.get_window_scaling(self)
|
||||
|
||||
def destroy(self):
|
||||
if self._scaling_type == "widget":
|
||||
if self.__scaling_type == "widget":
|
||||
ScalingTracker.remove_widget(self._set_scaling, self)
|
||||
elif self._scaling_type == "window":
|
||||
elif self.__scaling_type == "window":
|
||||
ScalingTracker.remove_window(self._set_scaling, self)
|
||||
|
||||
def _apply_widget_scaling(self, value: Union[int, float, str]) -> Union[float, str]:
|
||||
assert self._scaling_type == "widget"
|
||||
def _set_scaling(self, new_widget_scaling, new_window_scaling):
|
||||
""" can be overridden, but super method must be called at the beginning """
|
||||
self.__widget_scaling = new_widget_scaling
|
||||
self.__window_scaling = new_window_scaling
|
||||
|
||||
if isinstance(value, (int, float)):
|
||||
return value * self._widget_scaling
|
||||
else:
|
||||
return value
|
||||
def _get_widget_scaling(self) -> float:
|
||||
return self.__widget_scaling
|
||||
|
||||
def _get_window_scaling(self) -> float:
|
||||
return self.__window_scaling
|
||||
|
||||
def _apply_widget_scaling(self, value: Union[int, float]) -> Union[float]:
|
||||
assert self.__scaling_type == "widget"
|
||||
return value * self.__widget_scaling
|
||||
|
||||
def _reverse_widget_scaling(self, value: Union[int, float]) -> Union[float]:
|
||||
assert self.__scaling_type == "widget"
|
||||
return value / self.__widget_scaling
|
||||
|
||||
def _apply_window_scaling(self, value: Union[int, float]) -> int:
|
||||
assert self.__scaling_type == "window"
|
||||
return int(value * self.__window_scaling)
|
||||
|
||||
def _reverse_window_scaling(self, scaled_value: Union[int, float]) -> int:
|
||||
assert self.__scaling_type == "window"
|
||||
return int(scaled_value / self.__window_scaling)
|
||||
|
||||
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 """
|
||||
assert self._scaling_type == "widget"
|
||||
assert self.__scaling_type == "widget"
|
||||
|
||||
if type(font) == tuple:
|
||||
if len(font) == 1:
|
||||
return font
|
||||
elif len(font) == 2:
|
||||
return font[0], -abs(round(font[1] * self._widget_scaling))
|
||||
return font[0], -abs(round(font[1] * self.__widget_scaling))
|
||||
elif len(font) == 3:
|
||||
return font[0], -abs(round(font[1] * self._widget_scaling)), font[2]
|
||||
return font[0], -abs(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.create_scaled_tuple(self._widget_scaling)
|
||||
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:
|
||||
assert self._scaling_type == "widget"
|
||||
assert self.__scaling_type == "widget"
|
||||
|
||||
scaled_kwargs = copy.copy(kwargs)
|
||||
|
||||
# scale padding values
|
||||
if "pady" in scaled_kwargs:
|
||||
if isinstance(scaled_kwargs["pady"], (int, float, str)):
|
||||
if isinstance(scaled_kwargs["pady"], (int, float)):
|
||||
scaled_kwargs["pady"] = self._apply_widget_scaling(scaled_kwargs["pady"])
|
||||
elif isinstance(scaled_kwargs["pady"], tuple):
|
||||
scaled_kwargs["pady"] = tuple([self._apply_widget_scaling(v) for v in scaled_kwargs["pady"]])
|
||||
if "padx" in kwargs:
|
||||
if isinstance(scaled_kwargs["padx"], (int, float, str)):
|
||||
if isinstance(scaled_kwargs["padx"], (int, float)):
|
||||
scaled_kwargs["padx"] = self._apply_widget_scaling(scaled_kwargs["padx"])
|
||||
elif isinstance(scaled_kwargs["padx"], tuple):
|
||||
scaled_kwargs["padx"] = tuple([self._apply_widget_scaling(v) for v in scaled_kwargs["padx"]])
|
||||
|
||||
# scaled x, y values for place geometry manager
|
||||
if "x" in scaled_kwargs:
|
||||
scaled_kwargs["x"] = self._apply_widget_scaling(scaled_kwargs["x"])
|
||||
if "y" in scaled_kwargs:
|
||||
@@ -93,41 +133,29 @@ class CTkScalingBaseClass(ABC):
|
||||
return width, height, x, y
|
||||
|
||||
def _apply_geometry_scaling(self, geometry_string: str) -> str:
|
||||
assert self._scaling_type == "window"
|
||||
assert self.__scaling_type == "window"
|
||||
|
||||
width, height, x, y = self._parse_geometry_string(geometry_string)
|
||||
|
||||
if x is None and y is None: # no <x> and <y> in geometry_string
|
||||
return f"{round(width * self._window_scaling)}x{round(height * self._window_scaling)}"
|
||||
return f"{round(width * self.__window_scaling)}x{round(height * self.__window_scaling)}"
|
||||
|
||||
elif width is None and height is None: # no <width> and <height> in geometry_string
|
||||
return f"+{x}+{y}"
|
||||
|
||||
else:
|
||||
return f"{round(width * self._window_scaling)}x{round(height * self._window_scaling)}+{x}+{y}"
|
||||
return f"{round(width * self.__window_scaling)}x{round(height * self.__window_scaling)}+{x}+{y}"
|
||||
|
||||
def _reverse_geometry_scaling(self, scaled_geometry_string: str) -> str:
|
||||
assert self._scaling_type == "window"
|
||||
assert self.__scaling_type == "window"
|
||||
|
||||
width, height, x, y = self._parse_geometry_string(scaled_geometry_string)
|
||||
|
||||
if x is None and y is None: # no <x> and <y> in geometry_string
|
||||
return f"{round(width / self._window_scaling)}x{round(height / self._window_scaling)}"
|
||||
return f"{round(width / self.__window_scaling)}x{round(height / self.__window_scaling)}"
|
||||
|
||||
elif width is None and height is None: # no <width> and <height> in geometry_string
|
||||
return f"+{x}+{y}"
|
||||
|
||||
else:
|
||||
return f"{round(width / self._window_scaling)}x{round(height / self._window_scaling)}+{x}+{y}"
|
||||
|
||||
def _apply_window_scaling(self, value):
|
||||
assert self._scaling_type == "window"
|
||||
|
||||
if isinstance(value, (int, float)):
|
||||
return int(value * self._window_scaling)
|
||||
else:
|
||||
return value
|
||||
|
||||
@abstractmethod
|
||||
def _set_scaling(self, new_widget_scaling, new_window_scaling):
|
||||
return
|
||||
return f"{round(width / self.__window_scaling)}x{round(height / self.__window_scaling)}+{x}+{y}"
|
||||
|
||||
Reference in New Issue
Block a user