mirror of
https://github.com/TomSchimansky/CustomTkinter.git
synced 2023-08-10 21:13:13 +03:00
added CTkComboBox
This commit is contained in:
@@ -17,38 +17,48 @@ from ..theme_manager import ThemeManager
|
||||
|
||||
|
||||
class CTkBaseClass(tkinter.Frame):
|
||||
def __init__(self, *args, bg_color=None, width, height, **kwargs):
|
||||
""" Base class of every Ctk widget, handles the dimensions, bg_color,
|
||||
appearance_mode changes, scaling, bg changes of master if master is not a CTk widget """
|
||||
|
||||
def __init__(self,
|
||||
*args,
|
||||
bg_color: Union[str, tuple] = None,
|
||||
width: int,
|
||||
height: int,
|
||||
**kwargs):
|
||||
super().__init__(*args, width=width, height=height, **kwargs) # set desired size of underlying tkinter.Frame
|
||||
|
||||
self.bg_color = self.detect_color_of_master() if bg_color is None else bg_color
|
||||
# dimensions
|
||||
self._current_width = width # _current_width and _current_height in pixel, represent current size of the widget
|
||||
self._current_height = height # _current_width and _current_height are independent of the scale
|
||||
self._desired_width = width # _desired_width and _desired_height, represent desired size set by width and height
|
||||
self._desired_height = height
|
||||
|
||||
self.current_width = width # current_width and current_height in pixel, represent current size of the widget (not the desired size by init)
|
||||
self.current_height = height # current_width and current_height are independent of the scale
|
||||
self.desired_width = width
|
||||
self.desired_height = height
|
||||
# scaling
|
||||
ScalingTracker.add_widget(self.set_scaling, self) # add callback for automatic scaling changes
|
||||
self._widget_scaling = ScalingTracker.get_widget_scaling(self)
|
||||
self._spacing_scaling = ScalingTracker.get_spacing_scaling(self)
|
||||
|
||||
# add set_scaling method to callback list of ScalingTracker for automatic scaling changes
|
||||
ScalingTracker.add_widget(self.set_scaling, self)
|
||||
self.widget_scaling = ScalingTracker.get_widget_scaling(self)
|
||||
self.spacing_scaling = ScalingTracker.get_spacing_scaling(self)
|
||||
|
||||
super().configure(width=self.apply_widget_scaling(self.desired_width),
|
||||
height=self.apply_widget_scaling(self.desired_height))
|
||||
super().configure(width=self.apply_widget_scaling(self._desired_width),
|
||||
height=self.apply_widget_scaling(self._desired_height))
|
||||
|
||||
# save latest geometry function and kwargs
|
||||
class GeometryCallDict(TypedDict):
|
||||
function: Callable
|
||||
kwargs: dict
|
||||
|
||||
self.last_geometry_manager_call: Union[GeometryCallDict, None] = None
|
||||
self._last_geometry_manager_call: Union[GeometryCallDict, None] = None
|
||||
|
||||
# add set_appearance_mode method to callback list of AppearanceModeTracker for appearance mode changes
|
||||
AppearanceModeTracker.add(self.set_appearance_mode, self)
|
||||
self.appearance_mode = AppearanceModeTracker.get_mode() # 0: "Light" 1: "Dark"
|
||||
self._appearance_mode = AppearanceModeTracker.get_mode() # 0: "Light" 1: "Dark"
|
||||
|
||||
super().configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode))
|
||||
# background color
|
||||
self.bg_color = self.detect_color_of_master() if bg_color is None else bg_color
|
||||
|
||||
# overwrite configure methods of master when master is tkinter widget, so that bg changes get applied on child CTk widget too
|
||||
super().configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode))
|
||||
|
||||
# overwrite configure methods of master when master is tkinter widget, so that bg changes get applied on child CTk widget as well
|
||||
if isinstance(self.master, (tkinter.Tk, tkinter.Toplevel, tkinter.Frame)) and not isinstance(self.master, (CTkBaseClass, CTk, CTkToplevel)):
|
||||
master_old_configure = self.master.config
|
||||
|
||||
@@ -74,15 +84,15 @@ class CTkBaseClass(tkinter.Frame):
|
||||
super().destroy()
|
||||
|
||||
def place(self, **kwargs):
|
||||
self.last_geometry_manager_call = {"function": super().place, "kwargs": kwargs}
|
||||
self._last_geometry_manager_call = {"function": super().place, "kwargs": kwargs}
|
||||
super().place(**self.apply_argument_scaling(kwargs))
|
||||
|
||||
def pack(self, **kwargs):
|
||||
self.last_geometry_manager_call = {"function": super().pack, "kwargs": kwargs}
|
||||
self._last_geometry_manager_call = {"function": super().pack, "kwargs": kwargs}
|
||||
super().pack(**self.apply_argument_scaling(kwargs))
|
||||
|
||||
def grid(self, **kwargs):
|
||||
self.last_geometry_manager_call = {"function": super().grid, "kwargs": kwargs}
|
||||
self._last_geometry_manager_call = {"function": super().grid, "kwargs": kwargs}
|
||||
super().grid(**self.apply_argument_scaling(kwargs))
|
||||
|
||||
def apply_argument_scaling(self, kwargs: dict) -> dict:
|
||||
@@ -129,9 +139,9 @@ class CTkBaseClass(tkinter.Frame):
|
||||
|
||||
def update_dimensions_event(self, event):
|
||||
# only redraw if dimensions changed (for performance)
|
||||
if round(self.current_width) != round(event.width / self.widget_scaling) or round(self.current_height) != round(event.height / self.widget_scaling):
|
||||
self.current_width = (event.width / self.widget_scaling) # adjust current size according to new size given by event
|
||||
self.current_height = (event.height / self.widget_scaling) # current_width and current_height are independent of the scale
|
||||
if round(self._current_width) != round(event.width / self._widget_scaling) or round(self._current_height) != round(event.height / self._widget_scaling):
|
||||
self._current_width = (event.width / self._widget_scaling) # adjust current size according to new size given by event
|
||||
self._current_height = (event.height / self._widget_scaling) # _current_width and _current_height are independent of the scale
|
||||
|
||||
self.draw(no_color_updates=True) # faster drawing without color changes
|
||||
|
||||
@@ -164,9 +174,9 @@ class CTkBaseClass(tkinter.Frame):
|
||||
|
||||
def set_appearance_mode(self, mode_string):
|
||||
if mode_string.lower() == "dark":
|
||||
self.appearance_mode = 1
|
||||
self._appearance_mode = 1
|
||||
elif mode_string.lower() == "light":
|
||||
self.appearance_mode = 0
|
||||
self._appearance_mode = 0
|
||||
|
||||
if isinstance(self.master, (CTkBaseClass, CTk)) and hasattr(self.master, "fg_color"):
|
||||
self.bg_color = self.master.fg_color
|
||||
@@ -176,33 +186,33 @@ class CTkBaseClass(tkinter.Frame):
|
||||
self.draw()
|
||||
|
||||
def set_scaling(self, new_widget_scaling, new_spacing_scaling, new_window_scaling):
|
||||
self.widget_scaling = new_widget_scaling
|
||||
self.spacing_scaling = new_spacing_scaling
|
||||
self._widget_scaling = new_widget_scaling
|
||||
self._spacing_scaling = new_spacing_scaling
|
||||
|
||||
super().configure(width=self.apply_widget_scaling(self.desired_width),
|
||||
height=self.apply_widget_scaling(self.desired_height))
|
||||
super().configure(width=self.apply_widget_scaling(self._desired_width),
|
||||
height=self.apply_widget_scaling(self._desired_height))
|
||||
|
||||
if self.last_geometry_manager_call is not None:
|
||||
self.last_geometry_manager_call["function"](**self.apply_argument_scaling(self.last_geometry_manager_call["kwargs"]))
|
||||
if self._last_geometry_manager_call is not None:
|
||||
self._last_geometry_manager_call["function"](**self.apply_argument_scaling(self._last_geometry_manager_call["kwargs"]))
|
||||
|
||||
def set_dimensions(self, width=None, height=None):
|
||||
if width is not None:
|
||||
self.desired_width = width
|
||||
self._desired_width = width
|
||||
if height is not None:
|
||||
self.desired_height = height
|
||||
self._desired_height = height
|
||||
|
||||
super().configure(width=self.apply_widget_scaling(self.desired_width),
|
||||
height=self.apply_widget_scaling(self.desired_height))
|
||||
super().configure(width=self.apply_widget_scaling(self._desired_width),
|
||||
height=self.apply_widget_scaling(self._desired_height))
|
||||
|
||||
def apply_widget_scaling(self, value):
|
||||
def apply_widget_scaling(self, value: Union[int, float, str]) -> Union[float, str]:
|
||||
if isinstance(value, (int, float)):
|
||||
return value * self.widget_scaling
|
||||
return value * self._widget_scaling
|
||||
else:
|
||||
return value
|
||||
|
||||
def apply_spacing_scaling(self, value):
|
||||
def apply_spacing_scaling(self, value: Union[int, float, str]) -> Union[float, str]:
|
||||
if isinstance(value, (int, float)):
|
||||
return value * self.spacing_scaling
|
||||
return value * self._spacing_scaling
|
||||
else:
|
||||
return value
|
||||
|
||||
@@ -211,25 +221,23 @@ class CTkBaseClass(tkinter.Frame):
|
||||
font_list = list(font)
|
||||
for i in range(len(font_list)):
|
||||
if (type(font_list[i]) == int or type(font_list[i]) == float) and font_list[i] < 0:
|
||||
font_list[i] = int(font_list[i] * self.widget_scaling)
|
||||
font_list[i] = int(font_list[i] * self._widget_scaling)
|
||||
return tuple(font_list)
|
||||
|
||||
elif type(font) == str:
|
||||
for negative_number in re.findall(r" -\d* ", font):
|
||||
font = font.replace(negative_number, f" {int(int(negative_number) * self.widget_scaling)} ")
|
||||
font = font.replace(negative_number, f" {int(int(negative_number) * self._widget_scaling)} ")
|
||||
return font
|
||||
|
||||
elif isinstance(font, tkinter.font.Font):
|
||||
new_font_object = copy.copy(font)
|
||||
if font.cget("size") < 0:
|
||||
new_font_object.config(size=int(font.cget("size") * self.widget_scaling))
|
||||
new_font_object.config(size=int(font.cget("size") * self._widget_scaling))
|
||||
return new_font_object
|
||||
|
||||
else:
|
||||
return font
|
||||
|
||||
def draw(self, no_color_updates=False):
|
||||
def draw(self, no_color_updates: bool = False):
|
||||
""" abstract of draw method to be overridden """
|
||||
pass
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user