mirror of
https://github.com/TomSchimansky/CustomTkinter.git
synced 2023-08-10 21:13:13 +03:00
architecture fixes
This commit is contained in:
parent
302313916a
commit
7374e7a3bc
@ -70,6 +70,9 @@ from .windows.ctk_input_dialog import CTkInputDialog
|
|||||||
# font classes
|
# font classes
|
||||||
from .windows.widgets.font.ctk_font import CTkFont
|
from .windows.widgets.font.ctk_font import CTkFont
|
||||||
|
|
||||||
|
# image classes
|
||||||
|
from .windows.widgets.image.ctk_image import CTkImage
|
||||||
|
|
||||||
|
|
||||||
def set_appearance_mode(mode_string: str):
|
def set_appearance_mode(mode_string: str):
|
||||||
""" possible values: light, dark, system """
|
""" possible values: light, dark, system """
|
||||||
|
@ -63,10 +63,7 @@ class CTk(tkinter.Tk, CTkAppearanceModeBaseClass, CTkScalingBaseClass):
|
|||||||
self._iconify_called_before_window_exists = False # indicates if iconify() was called before window is first shown through update() or mainloop()
|
self._iconify_called_before_window_exists = False # indicates if iconify() was called before window is first shown through update() or mainloop()
|
||||||
|
|
||||||
if sys.platform.startswith("win"):
|
if sys.platform.startswith("win"):
|
||||||
if self._appearance_mode == 1:
|
self._windows_set_titlebar_color(self._get_appearance_mode())
|
||||||
self._windows_set_titlebar_color("dark")
|
|
||||||
else:
|
|
||||||
self._windows_set_titlebar_color("light")
|
|
||||||
|
|
||||||
self.bind('<Configure>', self._update_dimensions_event)
|
self.bind('<Configure>', self._update_dimensions_event)
|
||||||
self.bind('<FocusIn>', self._focus_in_event)
|
self.bind('<FocusIn>', self._focus_in_event)
|
||||||
@ -97,12 +94,12 @@ class CTk(tkinter.Tk, CTkAppearanceModeBaseClass, CTkScalingBaseClass):
|
|||||||
# detected_width = event.width
|
# detected_width = event.width
|
||||||
# detected_height = event.height
|
# detected_height = event.height
|
||||||
|
|
||||||
if self._current_width != round(detected_width / self._window_scaling) or self._current_height != round(detected_height / self._window_scaling):
|
if self._current_width != self._reverse_window_scaling(detected_width) or self._current_height != self._reverse_window_scaling(detected_height):
|
||||||
self._current_width = round(detected_width / self._window_scaling) # adjust current size according to new size given by event
|
self._current_width = self._reverse_window_scaling(detected_width) # adjust current size according to new size given by event
|
||||||
self._current_height = round(detected_height / self._window_scaling) # _current_width and _current_height are independent of the scale
|
self._current_height = self._reverse_window_scaling(detected_height) # _current_width and _current_height are independent of the scale
|
||||||
|
|
||||||
def _set_scaling(self, new_widget_scaling, new_window_scaling):
|
def _set_scaling(self, new_widget_scaling, new_window_scaling):
|
||||||
self._window_scaling = new_window_scaling
|
super()._set_scaling(new_widget_scaling, new_window_scaling)
|
||||||
|
|
||||||
# block update_dimensions_event to prevent current_width and current_height to get updated
|
# block update_dimensions_event to prevent current_width and current_height to get updated
|
||||||
self._block_update_dimensions_event = True
|
self._block_update_dimensions_event = True
|
||||||
@ -164,12 +161,9 @@ class CTk(tkinter.Tk, CTkAppearanceModeBaseClass, CTkScalingBaseClass):
|
|||||||
self._last_resizable_args = ([], {"width": width, "height": height})
|
self._last_resizable_args = ([], {"width": width, "height": height})
|
||||||
|
|
||||||
if sys.platform.startswith("win"):
|
if sys.platform.startswith("win"):
|
||||||
if self._appearance_mode == 1:
|
self._windows_set_titlebar_color(self._get_appearance_mode())
|
||||||
self._windows_set_titlebar_color("dark")
|
|
||||||
else:
|
|
||||||
self._windows_set_titlebar_color("light")
|
|
||||||
|
|
||||||
def minsize(self, width=None, height=None):
|
def minsize(self, width: int = None, height: int = None):
|
||||||
self._min_width = width
|
self._min_width = width
|
||||||
self._min_height = height
|
self._min_height = height
|
||||||
if self._current_width < width:
|
if self._current_width < width:
|
||||||
@ -178,7 +172,7 @@ class CTk(tkinter.Tk, CTkAppearanceModeBaseClass, CTkScalingBaseClass):
|
|||||||
self._current_height = height
|
self._current_height = height
|
||||||
super().minsize(self._apply_window_scaling(self._min_width), self._apply_window_scaling(self._min_height))
|
super().minsize(self._apply_window_scaling(self._min_width), self._apply_window_scaling(self._min_height))
|
||||||
|
|
||||||
def maxsize(self, width=None, height=None):
|
def maxsize(self, width: int = None, height: int = None):
|
||||||
self._max_width = width
|
self._max_width = width
|
||||||
self._max_height = height
|
self._max_height = height
|
||||||
if self._current_width > width:
|
if self._current_width > width:
|
||||||
@ -297,16 +291,10 @@ class CTk(tkinter.Tk, CTkAppearanceModeBaseClass, CTkScalingBaseClass):
|
|||||||
else:
|
else:
|
||||||
pass # wait for update or mainloop to be called
|
pass # wait for update or mainloop to be called
|
||||||
|
|
||||||
def _set_appearance_mode(self, mode_string):
|
def _set_appearance_mode(self, mode_string: str):
|
||||||
if mode_string.lower() == "dark":
|
super()._set_appearance_mode(mode_string)
|
||||||
self._appearance_mode = 1
|
|
||||||
elif mode_string.lower() == "light":
|
|
||||||
self._appearance_mode = 0
|
|
||||||
|
|
||||||
if sys.platform.startswith("win"):
|
if sys.platform.startswith("win"):
|
||||||
if self._appearance_mode == 1:
|
self._windows_set_titlebar_color(mode_string)
|
||||||
self._windows_set_titlebar_color("dark")
|
|
||||||
else:
|
|
||||||
self._windows_set_titlebar_color("light")
|
|
||||||
|
|
||||||
super().configure(bg=self._apply_appearance_mode(self._fg_color))
|
super().configure(bg=self._apply_appearance_mode(self._fg_color))
|
||||||
|
@ -60,10 +60,7 @@ class CTkToplevel(tkinter.Toplevel, CTkAppearanceModeBaseClass, CTkScalingBaseCl
|
|||||||
self._iconify_called_after_windows_set_titlebar_color = False # indicates if iconify() was called after windows_set_titlebar_color
|
self._iconify_called_after_windows_set_titlebar_color = False # indicates if iconify() was called after windows_set_titlebar_color
|
||||||
|
|
||||||
if sys.platform.startswith("win"):
|
if sys.platform.startswith("win"):
|
||||||
if self._appearance_mode == 1:
|
self._windows_set_titlebar_color(self._get_appearance_mode())
|
||||||
self._windows_set_titlebar_color("dark")
|
|
||||||
else:
|
|
||||||
self._windows_set_titlebar_color("light")
|
|
||||||
|
|
||||||
self.bind('<Configure>', self._update_dimensions_event)
|
self.bind('<Configure>', self._update_dimensions_event)
|
||||||
self.bind('<FocusIn>', self._focus_in_event)
|
self.bind('<FocusIn>', self._focus_in_event)
|
||||||
@ -85,12 +82,12 @@ class CTkToplevel(tkinter.Toplevel, CTkAppearanceModeBaseClass, CTkScalingBaseCl
|
|||||||
detected_width = self.winfo_width() # detect current window size
|
detected_width = self.winfo_width() # detect current window size
|
||||||
detected_height = self.winfo_height()
|
detected_height = self.winfo_height()
|
||||||
|
|
||||||
if self._current_width != round(detected_width / self._window_scaling) or self._current_height != round(detected_height / self._window_scaling):
|
if self._current_width != self._reverse_window_scaling(detected_width) or self._current_height != self._reverse_window_scaling(detected_height):
|
||||||
self._current_width = round(detected_width / self._window_scaling) # adjust current size according to new size given by event
|
self._current_width = self._reverse_window_scaling(detected_width) # adjust current size according to new size given by event
|
||||||
self._current_height = round(detected_height / self._window_scaling) # _current_width and _current_height are independent of the scale
|
self._current_height = self._reverse_window_scaling(detected_height) # _current_width and _current_height are independent of the scale
|
||||||
|
|
||||||
def _set_scaling(self, new_widget_scaling, new_window_scaling):
|
def _set_scaling(self, new_widget_scaling, new_window_scaling):
|
||||||
self._window_scaling = new_window_scaling
|
super()._set_scaling(new_widget_scaling, new_window_scaling)
|
||||||
|
|
||||||
# force new dimensions on window by using min, max, and geometry
|
# force new dimensions on window by using min, max, and geometry
|
||||||
super().minsize(self._apply_window_scaling(self._current_width), self._apply_window_scaling(self._current_height))
|
super().minsize(self._apply_window_scaling(self._current_width), self._apply_window_scaling(self._current_height))
|
||||||
@ -134,10 +131,7 @@ class CTkToplevel(tkinter.Toplevel, CTkAppearanceModeBaseClass, CTkScalingBaseCl
|
|||||||
self._last_resizable_args = ([], {"width": width, "height": height})
|
self._last_resizable_args = ([], {"width": width, "height": height})
|
||||||
|
|
||||||
if sys.platform.startswith("win"):
|
if sys.platform.startswith("win"):
|
||||||
if self._appearance_mode == 1:
|
self.after(10, lambda: self._windows_set_titlebar_color(self._get_appearance_mode()))
|
||||||
self.after(10, lambda: self._windows_set_titlebar_color("dark"))
|
|
||||||
else:
|
|
||||||
self.after(10, lambda: self._windows_set_titlebar_color("light"))
|
|
||||||
|
|
||||||
def minsize(self, width=None, height=None):
|
def minsize(self, width=None, height=None):
|
||||||
self._min_width = width
|
self._min_width = width
|
||||||
@ -259,15 +253,9 @@ class CTkToplevel(tkinter.Toplevel, CTkAppearanceModeBaseClass, CTkScalingBaseCl
|
|||||||
self._iconify_called_after_windows_set_titlebar_color = False
|
self._iconify_called_after_windows_set_titlebar_color = False
|
||||||
|
|
||||||
def _set_appearance_mode(self, mode_string):
|
def _set_appearance_mode(self, mode_string):
|
||||||
if mode_string.lower() == "dark":
|
super()._set_appearance_mode(mode_string)
|
||||||
self._appearance_mode = 1
|
|
||||||
elif mode_string.lower() == "light":
|
|
||||||
self._appearance_mode = 0
|
|
||||||
|
|
||||||
if sys.platform.startswith("win"):
|
if sys.platform.startswith("win"):
|
||||||
if self._appearance_mode == 1:
|
self._windows_set_titlebar_color(mode_string)
|
||||||
self._windows_set_titlebar_color("dark")
|
|
||||||
else:
|
|
||||||
self._windows_set_titlebar_color("light")
|
|
||||||
|
|
||||||
super().configure(bg=self._apply_appearance_mode(self._fg_color))
|
super().configure(bg=self._apply_appearance_mode(self._fg_color))
|
||||||
|
@ -1,27 +1,46 @@
|
|||||||
from typing import Union, Tuple, List
|
from typing import Union, Tuple, List
|
||||||
from abc import ABC, abstractmethod
|
|
||||||
|
|
||||||
from .appearance_mode_tracker import AppearanceModeTracker
|
from .appearance_mode_tracker import AppearanceModeTracker
|
||||||
|
|
||||||
|
|
||||||
class CTkAppearanceModeBaseClass(ABC):
|
class CTkAppearanceModeBaseClass:
|
||||||
|
"""
|
||||||
|
Super-class that manages the appearance mode. Methods:
|
||||||
|
|
||||||
|
- destroy() must be called when sub-class is destroyed
|
||||||
|
- _set_appearance_mode() abstractmethod, gets called when appearance mode changes, must be overridden
|
||||||
|
- _apply_appearance_mode()
|
||||||
|
|
||||||
|
"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
AppearanceModeTracker.add(self._set_appearance_mode, self)
|
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"
|
||||||
|
|
||||||
def destroy(self):
|
def destroy(self):
|
||||||
AppearanceModeTracker.remove(self._set_appearance_mode)
|
AppearanceModeTracker.remove(self._set_appearance_mode)
|
||||||
|
|
||||||
def _apply_appearance_mode(self, color: Union[str, Tuple[str, str], List[str]]) -> str:
|
def _set_appearance_mode(self, mode_string: str):
|
||||||
""" color can be either a single hex color string or a color name or it can be a
|
""" can be overridden but super method must be called at the beginning """
|
||||||
tuple color with (light_color, dark_color). The functions returns
|
if mode_string.lower() == "dark":
|
||||||
always a single color string """
|
self.__appearance_mode = 1
|
||||||
|
elif mode_string.lower() == "light":
|
||||||
|
self.__appearance_mode = 0
|
||||||
|
|
||||||
if type(color) == tuple or type(color) == list:
|
def _get_appearance_mode(self) -> str:
|
||||||
return color[self._appearance_mode]
|
""" get appearance mode as a string, 'light' or 'dark' """
|
||||||
|
if self.__appearance_mode == 0:
|
||||||
|
return "light"
|
||||||
|
else:
|
||||||
|
return "dark"
|
||||||
|
|
||||||
|
def _apply_appearance_mode(self, color: Union[str, Tuple[str, str], List[str]]) -> str:
|
||||||
|
"""
|
||||||
|
color can be either a single hex color string or a color name or it can be a
|
||||||
|
tuple color with (light_color, dark_color). The functions returns
|
||||||
|
always a single color string
|
||||||
|
"""
|
||||||
|
|
||||||
|
if isinstance(color, (tuple, list)):
|
||||||
|
return color[self.__appearance_mode]
|
||||||
else:
|
else:
|
||||||
return color
|
return color
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def _set_appearance_mode(self, mode_string: str):
|
|
||||||
return
|
|
||||||
|
@ -83,12 +83,12 @@ class DropdownMenu(tkinter.Menu, CTkAppearanceModeBaseClass):
|
|||||||
else:
|
else:
|
||||||
super().configure(tearoff=False,
|
super().configure(tearoff=False,
|
||||||
relief="flat",
|
relief="flat",
|
||||||
activebackground=ThemeManager._apply_appearance_mode(self._hover_color, self._appearance_mode),
|
activebackground=self._apply_appearance_mode(self._hover_color),
|
||||||
borderwidth=0,
|
borderwidth=0,
|
||||||
activeborderwidth=0,
|
activeborderwidth=0,
|
||||||
bg=ThemeManager._apply_appearance_mode(self._fg_color, self._appearance_mode),
|
bg=self._apply_appearance_mode(self._fg_color),
|
||||||
fg=ThemeManager._apply_appearance_mode(self._text_color, self._appearance_mode),
|
fg=self._apply_appearance_mode(self._text_color),
|
||||||
activeforeground=ThemeManager._apply_appearance_mode(self._text_color, self._appearance_mode),
|
activeforeground=self._apply_appearance_mode(self._text_color),
|
||||||
font=self._apply_font_scaling(self._font))
|
font=self._apply_font_scaling(self._font))
|
||||||
|
|
||||||
def _add_menu_commands(self):
|
def _add_menu_commands(self):
|
||||||
|
@ -98,7 +98,9 @@ class CTkBaseClass(tkinter.Frame, CTkAppearanceModeBaseClass, CTkScalingBaseClas
|
|||||||
CTkScalingBaseClass.destroy(self)
|
CTkScalingBaseClass.destroy(self)
|
||||||
|
|
||||||
def _draw(self, no_color_updates: bool = False):
|
def _draw(self, no_color_updates: bool = False):
|
||||||
return
|
""" can be overridden but super method must be called """
|
||||||
|
if no_color_updates is False:
|
||||||
|
super().configure(bg=self._apply_appearance_mode(self._bg_color))
|
||||||
|
|
||||||
def config(self, *args, **kwargs):
|
def config(self, *args, **kwargs):
|
||||||
raise AttributeError("'config' is not implemented for CTk widgets. For consistency, always use 'configure' instead.")
|
raise AttributeError("'config' is not implemented for CTk widgets. For consistency, always use 'configure' instead.")
|
||||||
@ -164,9 +166,9 @@ class CTkBaseClass(tkinter.Frame, CTkAppearanceModeBaseClass, CTkScalingBaseClas
|
|||||||
|
|
||||||
def _update_dimensions_event(self, event):
|
def _update_dimensions_event(self, event):
|
||||||
# only redraw if dimensions changed (for performance), independent of scaling
|
# only redraw if dimensions changed (for performance), independent of scaling
|
||||||
if round(self._current_width) != round(event.width / self._widget_scaling) or round(self._current_height) != round(event.height / self._widget_scaling):
|
if round(self._current_width) != round(self._reverse_widget_scaling(event.width)) or round(self._current_height) != round(self._reverse_widget_scaling(event.height)):
|
||||||
self._current_width = (event.width / self._widget_scaling) # adjust current size according to new size given by event
|
self._current_width = self._reverse_widget_scaling(event.width) # 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._current_height = self._reverse_widget_scaling(event.height) # _current_width and _current_height are independent of the scale
|
||||||
|
|
||||||
self._draw(no_color_updates=True) # faster drawing without color changes
|
self._draw(no_color_updates=True) # faster drawing without color changes
|
||||||
|
|
||||||
@ -198,16 +200,11 @@ class CTkBaseClass(tkinter.Frame, CTkAppearanceModeBaseClass, CTkScalingBaseClas
|
|||||||
return "#FFFFFF", "#000000"
|
return "#FFFFFF", "#000000"
|
||||||
|
|
||||||
def _set_appearance_mode(self, mode_string):
|
def _set_appearance_mode(self, mode_string):
|
||||||
if mode_string.lower() == "dark":
|
super()._set_appearance_mode(mode_string)
|
||||||
self._appearance_mode = 1
|
|
||||||
elif mode_string.lower() == "light":
|
|
||||||
self._appearance_mode = 0
|
|
||||||
|
|
||||||
super().configure(bg=self._apply_appearance_mode(self._bg_color))
|
|
||||||
self._draw()
|
self._draw()
|
||||||
|
|
||||||
def _set_scaling(self, new_widget_scaling, new_window_scaling):
|
def _set_scaling(self, new_widget_scaling, new_window_scaling):
|
||||||
self._widget_scaling = new_widget_scaling
|
super()._set_scaling(new_widget_scaling, new_window_scaling)
|
||||||
|
|
||||||
super().configure(width=self._apply_widget_scaling(self._desired_width),
|
super().configure(width=self._apply_widget_scaling(self._desired_width),
|
||||||
height=self._apply_widget_scaling(self._desired_height))
|
height=self._apply_widget_scaling(self._desired_height))
|
||||||
|
@ -48,7 +48,7 @@ class CTkButton(CTkBaseClass):
|
|||||||
anchor: str = "center",
|
anchor: str = "center",
|
||||||
**kwargs):
|
**kwargs):
|
||||||
|
|
||||||
# transfer basic functionality (_bg_color, size, _appearance_mode, scaling) to CTkBaseClass
|
# transfer basic functionality (_bg_color, size, __appearance_mode, scaling) to CTkBaseClass
|
||||||
super().__init__(master=master, bg_color=bg_color, width=width, height=height, **kwargs)
|
super().__init__(master=master, bg_color=bg_color, width=width, height=height, **kwargs)
|
||||||
|
|
||||||
# color
|
# color
|
||||||
@ -74,6 +74,8 @@ class CTkButton(CTkBaseClass):
|
|||||||
self._image_label: Union[tkinter.Label, None] = None
|
self._image_label: Union[tkinter.Label, None] = None
|
||||||
self._text = text
|
self._text = text
|
||||||
self._text_label: Union[tkinter.Label, None] = None
|
self._text_label: Union[tkinter.Label, None] = None
|
||||||
|
if isinstance(self._image, CTkImage):
|
||||||
|
self._image.add_configure_callback(self._update_image)
|
||||||
|
|
||||||
# font
|
# font
|
||||||
self._font = CTkFont() if font == "default_theme" else self._check_font_type(font)
|
self._font = CTkFont() if font == "default_theme" else self._check_font_type(font)
|
||||||
@ -135,12 +137,19 @@ class CTkButton(CTkBaseClass):
|
|||||||
self._canvas.grid_forget()
|
self._canvas.grid_forget()
|
||||||
self._canvas.grid(row=0, column=0, rowspan=5, columnspan=5, sticky="nsew")
|
self._canvas.grid(row=0, column=0, rowspan=5, columnspan=5, sticky="nsew")
|
||||||
|
|
||||||
|
def _update_image(self):
|
||||||
|
if self._image_label is not None:
|
||||||
|
self._image_label.configure(image=self._image.create_scaled_photo_image(self._get_widget_scaling(),
|
||||||
|
self._get_appearance_mode()))
|
||||||
|
|
||||||
def destroy(self):
|
def destroy(self):
|
||||||
if isinstance(self._font, CTkFont):
|
if isinstance(self._font, CTkFont):
|
||||||
self._font.remove_size_configure_callback(self._update_font)
|
self._font.remove_size_configure_callback(self._update_font)
|
||||||
super().destroy()
|
super().destroy()
|
||||||
|
|
||||||
def _draw(self, no_color_updates=False):
|
def _draw(self, no_color_updates=False):
|
||||||
|
super()._draw(no_color_updates)
|
||||||
|
|
||||||
if self._background_corner_colors is not None:
|
if self._background_corner_colors is not None:
|
||||||
self._draw_engine.draw_background_corners(self._apply_widget_scaling(self._current_width),
|
self._draw_engine.draw_background_corners(self._apply_widget_scaling(self._current_width),
|
||||||
self._apply_widget_scaling(self._current_height))
|
self._apply_widget_scaling(self._current_height))
|
||||||
@ -233,7 +242,7 @@ class CTkButton(CTkBaseClass):
|
|||||||
else:
|
else:
|
||||||
self._image_label.configure(bg=self._apply_appearance_mode(self._fg_color))
|
self._image_label.configure(bg=self._apply_appearance_mode(self._fg_color))
|
||||||
|
|
||||||
self._image_label.configure(image=self._image) # set image
|
self._update_image() # set image
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# delete text_label if no text given
|
# delete text_label if no text given
|
||||||
@ -354,7 +363,6 @@ class CTkButton(CTkBaseClass):
|
|||||||
require_redraw = True # text_label will be created in .draw()
|
require_redraw = True # text_label will be created in .draw()
|
||||||
else:
|
else:
|
||||||
self._text_label.configure(text=self._text)
|
self._text_label.configure(text=self._text)
|
||||||
self._create_grid()
|
|
||||||
|
|
||||||
if "font" in kwargs:
|
if "font" in kwargs:
|
||||||
if isinstance(self._font, CTkFont):
|
if isinstance(self._font, CTkFont):
|
||||||
@ -371,8 +379,11 @@ class CTkButton(CTkBaseClass):
|
|||||||
self._text_label.configure(textvariable=self._textvariable)
|
self._text_label.configure(textvariable=self._textvariable)
|
||||||
|
|
||||||
if "image" in kwargs:
|
if "image" in kwargs:
|
||||||
|
if isinstance(self._image, CTkImage):
|
||||||
|
self._image.remove_configure_callback(self._update_image)
|
||||||
self._image = kwargs.pop("image")
|
self._image = kwargs.pop("image")
|
||||||
self._create_grid()
|
if isinstance(self._image, CTkImage):
|
||||||
|
self._image.add_configure_callback(self._update_image)
|
||||||
require_redraw = True
|
require_redraw = True
|
||||||
|
|
||||||
if "state" in kwargs:
|
if "state" in kwargs:
|
||||||
|
@ -43,7 +43,7 @@ class CTkCheckBox(CTkBaseClass):
|
|||||||
variable: tkinter.Variable = None,
|
variable: tkinter.Variable = None,
|
||||||
**kwargs):
|
**kwargs):
|
||||||
|
|
||||||
# transfer basic functionality (_bg_color, size, _appearance_mode, scaling) to CTkBaseClass
|
# transfer basic functionality (_bg_color, size, __appearance_mode, scaling) to CTkBaseClass
|
||||||
super().__init__(master=master, bg_color=bg_color, width=width, height=height, **kwargs)
|
super().__init__(master=master, bg_color=bg_color, width=width, height=height, **kwargs)
|
||||||
|
|
||||||
# dimensions
|
# dimensions
|
||||||
@ -169,6 +169,8 @@ class CTkCheckBox(CTkBaseClass):
|
|||||||
super().destroy()
|
super().destroy()
|
||||||
|
|
||||||
def _draw(self, no_color_updates=False):
|
def _draw(self, no_color_updates=False):
|
||||||
|
super()._draw(no_color_updates)
|
||||||
|
|
||||||
requires_recoloring_1 = self._draw_engine.draw_rounded_rect_with_border(self._apply_widget_scaling(self._checkbox_width),
|
requires_recoloring_1 = self._draw_engine.draw_rounded_rect_with_border(self._apply_widget_scaling(self._checkbox_width),
|
||||||
self._apply_widget_scaling(self._checkbox_height),
|
self._apply_widget_scaling(self._checkbox_height),
|
||||||
self._apply_widget_scaling(self._corner_radius),
|
self._apply_widget_scaling(self._corner_radius),
|
||||||
|
@ -44,7 +44,7 @@ class CTkComboBox(CTkBaseClass):
|
|||||||
justify: str = "left",
|
justify: str = "left",
|
||||||
**kwargs):
|
**kwargs):
|
||||||
|
|
||||||
# transfer basic functionality (_bg_color, size, _appearance_mode, scaling) to CTkBaseClass
|
# transfer basic functionality (_bg_color, size, __appearance_mode, scaling) to CTkBaseClass
|
||||||
super().__init__(master=master, bg_color=bg_color, width=width, height=height, **kwargs)
|
super().__init__(master=master, bg_color=bg_color, width=width, height=height, **kwargs)
|
||||||
|
|
||||||
# color variables
|
# color variables
|
||||||
@ -167,6 +167,8 @@ class CTkComboBox(CTkBaseClass):
|
|||||||
super().destroy()
|
super().destroy()
|
||||||
|
|
||||||
def _draw(self, no_color_updates=False):
|
def _draw(self, no_color_updates=False):
|
||||||
|
super()._draw(no_color_updates)
|
||||||
|
|
||||||
left_section_width = self._current_width - self._current_height
|
left_section_width = self._current_width - self._current_height
|
||||||
requires_recoloring = self.draw_engine.draw_rounded_rect_with_border_vertical_split(self._apply_widget_scaling(self._current_width),
|
requires_recoloring = self.draw_engine.draw_rounded_rect_with_border_vertical_split(self._apply_widget_scaling(self._current_width),
|
||||||
self._apply_widget_scaling(self._current_height),
|
self._apply_widget_scaling(self._current_height),
|
||||||
|
@ -148,6 +148,8 @@ class CTkEntry(CTkBaseClass):
|
|||||||
super().destroy()
|
super().destroy()
|
||||||
|
|
||||||
def _draw(self, no_color_updates=False):
|
def _draw(self, no_color_updates=False):
|
||||||
|
super()._draw(no_color_updates)
|
||||||
|
|
||||||
self._canvas.configure(bg=self._apply_appearance_mode(self._bg_color))
|
self._canvas.configure(bg=self._apply_appearance_mode(self._bg_color))
|
||||||
|
|
||||||
requires_recoloring = self._draw_engine.draw_rounded_rect_with_border(self._apply_widget_scaling(self._current_width),
|
requires_recoloring = self._draw_engine.draw_rounded_rect_with_border(self._apply_widget_scaling(self._current_width),
|
||||||
|
@ -29,7 +29,7 @@ class CTkFrame(CTkBaseClass):
|
|||||||
overwrite_preferred_drawing_method: str = None,
|
overwrite_preferred_drawing_method: str = None,
|
||||||
**kwargs):
|
**kwargs):
|
||||||
|
|
||||||
# transfer basic functionality (_bg_color, size, _appearance_mode, scaling) to CTkBaseClass
|
# transfer basic functionality (_bg_color, size, __appearance_mode, scaling) to CTkBaseClass
|
||||||
super().__init__(master=master, bg_color=bg_color, width=width, height=height, **kwargs)
|
super().__init__(master=master, bg_color=bg_color, width=width, height=height, **kwargs)
|
||||||
|
|
||||||
# color
|
# color
|
||||||
@ -91,6 +91,8 @@ class CTkFrame(CTkBaseClass):
|
|||||||
self._draw()
|
self._draw()
|
||||||
|
|
||||||
def _draw(self, no_color_updates=False):
|
def _draw(self, no_color_updates=False):
|
||||||
|
super()._draw(no_color_updates)
|
||||||
|
|
||||||
if not self._canvas.winfo_exists():
|
if not self._canvas.winfo_exists():
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ class CTkLabel(CTkBaseClass):
|
|||||||
anchor: str = "center", # label anchor: center, n, e, s, w
|
anchor: str = "center", # label anchor: center, n, e, s, w
|
||||||
**kwargs):
|
**kwargs):
|
||||||
|
|
||||||
# transfer basic functionality (_bg_color, size, _appearance_mode, scaling) to CTkBaseClass
|
# transfer basic functionality (_bg_color, size, __appearance_mode, scaling) to CTkBaseClass
|
||||||
super().__init__(master=master, bg_color=bg_color, width=width, height=height)
|
super().__init__(master=master, bg_color=bg_color, width=width, height=height)
|
||||||
|
|
||||||
# color
|
# color
|
||||||
@ -117,6 +117,8 @@ class CTkLabel(CTkBaseClass):
|
|||||||
super().destroy()
|
super().destroy()
|
||||||
|
|
||||||
def _draw(self, no_color_updates=False):
|
def _draw(self, no_color_updates=False):
|
||||||
|
super()._draw(no_color_updates)
|
||||||
|
|
||||||
requires_recoloring = self._draw_engine.draw_rounded_rect_with_border(self._apply_widget_scaling(self._current_width),
|
requires_recoloring = self._draw_engine.draw_rounded_rect_with_border(self._apply_widget_scaling(self._current_width),
|
||||||
self._apply_widget_scaling(self._current_height),
|
self._apply_widget_scaling(self._current_height),
|
||||||
self._apply_widget_scaling(self._corner_radius),
|
self._apply_widget_scaling(self._corner_radius),
|
||||||
|
@ -43,7 +43,7 @@ class CTkOptionMenu(CTkBaseClass):
|
|||||||
anchor: str = "w",
|
anchor: str = "w",
|
||||||
**kwargs):
|
**kwargs):
|
||||||
|
|
||||||
# transfer basic functionality (_bg_color, size, _appearance_mode, scaling) to CTkBaseClass
|
# transfer basic functionality (_bg_color, size, __appearance_mode, scaling) to CTkBaseClass
|
||||||
super().__init__(master=master, bg_color=bg_color, width=width, height=height, **kwargs)
|
super().__init__(master=master, bg_color=bg_color, width=width, height=height, **kwargs)
|
||||||
|
|
||||||
# color variables
|
# color variables
|
||||||
@ -180,6 +180,8 @@ class CTkOptionMenu(CTkBaseClass):
|
|||||||
super().destroy()
|
super().destroy()
|
||||||
|
|
||||||
def _draw(self, no_color_updates=False):
|
def _draw(self, no_color_updates=False):
|
||||||
|
super()._draw(no_color_updates)
|
||||||
|
|
||||||
left_section_width = self._current_width - self._current_height
|
left_section_width = self._current_width - self._current_height
|
||||||
requires_recoloring = self._draw_engine.draw_rounded_rect_with_border_vertical_split(self._apply_widget_scaling(self._current_width),
|
requires_recoloring = self._draw_engine.draw_rounded_rect_with_border_vertical_split(self._apply_widget_scaling(self._current_width),
|
||||||
self._apply_widget_scaling(self._current_height),
|
self._apply_widget_scaling(self._current_height),
|
||||||
|
@ -46,7 +46,7 @@ class CTkProgressBar(CTkBaseClass):
|
|||||||
else:
|
else:
|
||||||
height = 8
|
height = 8
|
||||||
|
|
||||||
# transfer basic functionality (_bg_color, size, _appearance_mode, scaling) to CTkBaseClass
|
# transfer basic functionality (_bg_color, size, __appearance_mode, scaling) to CTkBaseClass
|
||||||
super().__init__(master=master, bg_color=bg_color, width=width, height=height, **kwargs)
|
super().__init__(master=master, bg_color=bg_color, width=width, height=height, **kwargs)
|
||||||
|
|
||||||
# color
|
# color
|
||||||
@ -110,6 +110,8 @@ class CTkProgressBar(CTkBaseClass):
|
|||||||
super().destroy()
|
super().destroy()
|
||||||
|
|
||||||
def _draw(self, no_color_updates=False):
|
def _draw(self, no_color_updates=False):
|
||||||
|
super()._draw(no_color_updates)
|
||||||
|
|
||||||
if self._orientation.lower() == "horizontal":
|
if self._orientation.lower() == "horizontal":
|
||||||
orientation = "w"
|
orientation = "w"
|
||||||
elif self._orientation.lower() == "vertical":
|
elif self._orientation.lower() == "vertical":
|
||||||
|
@ -42,7 +42,7 @@ class CTkRadioButton(CTkBaseClass):
|
|||||||
command: Callable = None,
|
command: Callable = None,
|
||||||
**kwargs):
|
**kwargs):
|
||||||
|
|
||||||
# transfer basic functionality (_bg_color, size, _appearance_mode, scaling) to CTkBaseClass
|
# transfer basic functionality (_bg_color, size, __appearance_mode, scaling) to CTkBaseClass
|
||||||
super().__init__(master=master, bg_color=bg_color, width=width, height=height, **kwargs)
|
super().__init__(master=master, bg_color=bg_color, width=width, height=height, **kwargs)
|
||||||
|
|
||||||
# dimensions
|
# dimensions
|
||||||
@ -163,6 +163,8 @@ class CTkRadioButton(CTkBaseClass):
|
|||||||
super().destroy()
|
super().destroy()
|
||||||
|
|
||||||
def _draw(self, no_color_updates=False):
|
def _draw(self, no_color_updates=False):
|
||||||
|
super()._draw(no_color_updates)
|
||||||
|
|
||||||
requires_recoloring = self._draw_engine.draw_rounded_rect_with_border(self._apply_widget_scaling(self._radiobutton_width),
|
requires_recoloring = self._draw_engine.draw_rounded_rect_with_border(self._apply_widget_scaling(self._radiobutton_width),
|
||||||
self._apply_widget_scaling(self._radiobutton_height),
|
self._apply_widget_scaling(self._radiobutton_height),
|
||||||
self._apply_widget_scaling(self._corner_radius),
|
self._apply_widget_scaling(self._corner_radius),
|
||||||
|
@ -44,7 +44,7 @@ class CTkScrollbar(CTkBaseClass):
|
|||||||
else:
|
else:
|
||||||
height = 200
|
height = 200
|
||||||
|
|
||||||
# transfer basic functionality (_bg_color, size, _appearance_mode, scaling) to CTkBaseClass
|
# transfer basic functionality (_bg_color, size, __appearance_mode, scaling) to CTkBaseClass
|
||||||
super().__init__(master=master, bg_color=bg_color, width=width, height=height, **kwargs)
|
super().__init__(master=master, bg_color=bg_color, width=width, height=height, **kwargs)
|
||||||
|
|
||||||
# color
|
# color
|
||||||
@ -118,6 +118,8 @@ class CTkScrollbar(CTkBaseClass):
|
|||||||
return self._start_value, self._end_value
|
return self._start_value, self._end_value
|
||||||
|
|
||||||
def _draw(self, no_color_updates=False):
|
def _draw(self, no_color_updates=False):
|
||||||
|
super()._draw(no_color_updates)
|
||||||
|
|
||||||
corrected_start_value, corrected_end_value = self._get_scrollbar_values_for_minimum_pixel_size()
|
corrected_start_value, corrected_end_value = self._get_scrollbar_values_for_minimum_pixel_size()
|
||||||
requires_recoloring = self._draw_engine.draw_rounded_scrollbar(self._apply_widget_scaling(self._current_width),
|
requires_recoloring = self._draw_engine.draw_rounded_scrollbar(self._apply_widget_scaling(self._current_width),
|
||||||
self._apply_widget_scaling(self._current_height),
|
self._apply_widget_scaling(self._current_height),
|
||||||
@ -219,9 +221,9 @@ class CTkScrollbar(CTkBaseClass):
|
|||||||
|
|
||||||
def _clicked(self, event):
|
def _clicked(self, event):
|
||||||
if self._orientation == "vertical":
|
if self._orientation == "vertical":
|
||||||
value = ((event.y - self._border_spacing) / (self._current_height - 2 * self._border_spacing)) / self._widget_scaling
|
value = self._reverse_widget_scaling(((event.y - self._border_spacing) / (self._current_height - 2 * self._border_spacing)))
|
||||||
else:
|
else:
|
||||||
value = ((event.x - self._border_spacing) / (self._current_width - 2 * self._border_spacing)) / self._widget_scaling
|
value = self._reverse_widget_scaling(((event.x - self._border_spacing) / (self._current_width - 2 * self._border_spacing)))
|
||||||
|
|
||||||
current_scrollbar_length = self._end_value - self._start_value
|
current_scrollbar_length = self._end_value - self._start_value
|
||||||
value = max(current_scrollbar_length / 2, min(value, 1 - (current_scrollbar_length / 2)))
|
value = max(current_scrollbar_length / 2, min(value, 1 - (current_scrollbar_length / 2)))
|
||||||
|
@ -52,7 +52,7 @@ class CTkSlider(CTkBaseClass):
|
|||||||
else:
|
else:
|
||||||
height = 16
|
height = 16
|
||||||
|
|
||||||
# transfer basic functionality (_bg_color, size, _appearance_mode, scaling) to CTkBaseClass
|
# transfer basic functionality (_bg_color, size, __appearance_mode, scaling) to CTkBaseClass
|
||||||
super().__init__(master=master, bg_color=bg_color, width=width, height=height, **kwargs)
|
super().__init__(master=master, bg_color=bg_color, width=width, height=height, **kwargs)
|
||||||
|
|
||||||
# color
|
# color
|
||||||
@ -145,6 +145,8 @@ class CTkSlider(CTkBaseClass):
|
|||||||
self.configure(cursor="arrow")
|
self.configure(cursor="arrow")
|
||||||
|
|
||||||
def _draw(self, no_color_updates=False):
|
def _draw(self, no_color_updates=False):
|
||||||
|
super()._draw(no_color_updates)
|
||||||
|
|
||||||
if self._orientation.lower() == "horizontal":
|
if self._orientation.lower() == "horizontal":
|
||||||
orientation = "w"
|
orientation = "w"
|
||||||
elif self._orientation.lower() == "vertical":
|
elif self._orientation.lower() == "vertical":
|
||||||
|
@ -45,7 +45,7 @@ class CTkSwitch(CTkBaseClass):
|
|||||||
state: str = tkinter.NORMAL,
|
state: str = tkinter.NORMAL,
|
||||||
**kwargs):
|
**kwargs):
|
||||||
|
|
||||||
# transfer basic functionality (_bg_color, size, _appearance_mode, scaling) to CTkBaseClass
|
# transfer basic functionality (_bg_color, size, __appearance_mode, scaling) to CTkBaseClass
|
||||||
super().__init__(master=master, bg_color=bg_color, width=width, height=height, **kwargs)
|
super().__init__(master=master, bg_color=bg_color, width=width, height=height, **kwargs)
|
||||||
|
|
||||||
# dimensions
|
# dimensions
|
||||||
@ -193,6 +193,7 @@ class CTkSwitch(CTkBaseClass):
|
|||||||
self._text_label.configure(cursor="hand2")
|
self._text_label.configure(cursor="hand2")
|
||||||
|
|
||||||
def _draw(self, no_color_updates=False):
|
def _draw(self, no_color_updates=False):
|
||||||
|
super()._draw(no_color_updates)
|
||||||
|
|
||||||
if self._check_state is True:
|
if self._check_state is True:
|
||||||
requires_recoloring = self._draw_engine.draw_rounded_slider_with_border_and_button(self._apply_widget_scaling(self._switch_width),
|
requires_recoloring = self._draw_engine.draw_rounded_slider_with_border_and_button(self._apply_widget_scaling(self._switch_width),
|
||||||
|
@ -188,6 +188,8 @@ class CTkTabview(CTkBaseClass):
|
|||||||
return new_tab
|
return new_tab
|
||||||
|
|
||||||
def _draw(self, no_color_updates: bool = False):
|
def _draw(self, no_color_updates: bool = False):
|
||||||
|
super()._draw(no_color_updates)
|
||||||
|
|
||||||
if not self._canvas.winfo_exists():
|
if not self._canvas.winfo_exists():
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ class CTkTextbox(CTkBaseClass):
|
|||||||
activate_scrollbars: bool = True,
|
activate_scrollbars: bool = True,
|
||||||
**kwargs):
|
**kwargs):
|
||||||
|
|
||||||
# transfer basic functionality (_bg_color, size, _appearance_mode, scaling) to CTkBaseClass
|
# transfer basic functionality (_bg_color, size, __appearance_mode, scaling) to CTkBaseClass
|
||||||
super().__init__(master=master, bg_color=bg_color, width=width, height=height)
|
super().__init__(master=master, bg_color=bg_color, width=width, height=height)
|
||||||
|
|
||||||
# color
|
# color
|
||||||
@ -210,6 +210,7 @@ class CTkTextbox(CTkBaseClass):
|
|||||||
super().destroy()
|
super().destroy()
|
||||||
|
|
||||||
def _draw(self, no_color_updates=False):
|
def _draw(self, no_color_updates=False):
|
||||||
|
super()._draw(no_color_updates)
|
||||||
|
|
||||||
if not self._canvas.winfo_exists():
|
if not self._canvas.winfo_exists():
|
||||||
return
|
return
|
||||||
|
@ -18,11 +18,16 @@ class CTkImage:
|
|||||||
|
|
||||||
_checked_PIL_import = False
|
_checked_PIL_import = False
|
||||||
|
|
||||||
def __init__(self, light_image: Image.Image = None, dark_image: Image.Image = None, size: Tuple[int, int] = None):
|
def __init__(self,
|
||||||
|
light_image: Image.Image = None,
|
||||||
|
dark_image: Image.Image = None,
|
||||||
|
size: Tuple[int, int] = (20, 20)):
|
||||||
|
|
||||||
if not self._checked_PIL_import:
|
if not self._checked_PIL_import:
|
||||||
self._check_pil_import()
|
self._check_pil_import()
|
||||||
|
|
||||||
self._light_image = light_image
|
self._light_image = light_image
|
||||||
|
print(self._light_image)
|
||||||
self._dark_image = dark_image
|
self._dark_image = dark_image
|
||||||
self._check_images()
|
self._check_images()
|
||||||
self._size = size
|
self._size = size
|
||||||
@ -33,8 +38,10 @@ class CTkImage:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _check_pil_import(cls):
|
def _check_pil_import(cls):
|
||||||
if "Image" not in dir() or "ImageTk" not in dir():
|
try:
|
||||||
raise ImportError("CTkImage: Couldn't import PIL.Image or PIL.ImageTk. PIL must be installed.")
|
_, _ = Image, ImageTk
|
||||||
|
except NameError:
|
||||||
|
raise ImportError("PIL.Image and PIL.ImageTk couldn't be imported")
|
||||||
|
|
||||||
def add_configure_callback(self, callback: Callable):
|
def add_configure_callback(self, callback: Callable):
|
||||||
""" add function, that gets called when image got configured """
|
""" add function, that gets called when image got configured """
|
||||||
@ -84,7 +91,7 @@ class CTkImage:
|
|||||||
raise ValueError(f"CTkImage: light_image size {self._light_image.size} must be the same as dark_image size {self._dark_image.size}.")
|
raise ValueError(f"CTkImage: light_image size {self._light_image.size} must be the same as dark_image size {self._dark_image.size}.")
|
||||||
|
|
||||||
def _get_scaled_size(self, widget_scaling: float) -> Tuple[int, int]:
|
def _get_scaled_size(self, widget_scaling: float) -> Tuple[int, int]:
|
||||||
return round(self._size[0] * widget_scaling), round(self._size[0] * widget_scaling)
|
return round(self._size[0] * widget_scaling), round(self._size[1] * widget_scaling)
|
||||||
|
|
||||||
def _get_scaled_light_photo_image(self, scaled_size: Tuple[int, int]) -> ImageTk.PhotoImage:
|
def _get_scaled_light_photo_image(self, scaled_size: Tuple[int, int]) -> ImageTk.PhotoImage:
|
||||||
if scaled_size in self._scaled_light_photo_images:
|
if scaled_size in self._scaled_light_photo_images:
|
||||||
@ -100,17 +107,18 @@ class CTkImage:
|
|||||||
self._scaled_dark_photo_images[scaled_size] = ImageTk.PhotoImage(self._dark_image.resize(scaled_size))
|
self._scaled_dark_photo_images[scaled_size] = ImageTk.PhotoImage(self._dark_image.resize(scaled_size))
|
||||||
return self._scaled_dark_photo_images[scaled_size]
|
return self._scaled_dark_photo_images[scaled_size]
|
||||||
|
|
||||||
def create_scaled_photo_image(self, widget_scaling: float, appearance_mode: int) -> ImageTk.PhotoImage:
|
def create_scaled_photo_image(self, widget_scaling: float, appearance_mode: str) -> ImageTk.PhotoImage:
|
||||||
scaled_size = self._get_scaled_size(widget_scaling)
|
scaled_size = self._get_scaled_size(widget_scaling)
|
||||||
|
print(scaled_size)
|
||||||
|
|
||||||
if appearance_mode == 0 and self._light_image is not None:
|
if appearance_mode == "light" and self._light_image is not None:
|
||||||
return self._get_scaled_light_photo_image(scaled_size)
|
return self._get_scaled_light_photo_image(scaled_size)
|
||||||
elif appearance_mode == 0 and self._light_image is None:
|
elif appearance_mode == "light" and self._light_image is None:
|
||||||
return self._get_scaled_dark_photo_image(scaled_size)
|
return self._get_scaled_dark_photo_image(scaled_size)
|
||||||
|
|
||||||
elif appearance_mode == 1 and self._dark_image is not None:
|
elif appearance_mode == "dark" and self._dark_image is not None:
|
||||||
return self._get_scaled_dark_photo_image(scaled_size)
|
return self._get_scaled_dark_photo_image(scaled_size)
|
||||||
elif appearance_mode == 1 and self._dark_image is None:
|
elif appearance_mode == "dark" and self._dark_image is None:
|
||||||
return self._get_scaled_light_photo_image(scaled_size)
|
return self._get_scaled_light_photo_image(scaled_size)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
|
import tkinter
|
||||||
from typing import Union, Tuple
|
from typing import Union, Tuple
|
||||||
from abc import ABC, abstractmethod
|
|
||||||
import copy
|
import copy
|
||||||
import re
|
import re
|
||||||
try:
|
try:
|
||||||
@ -9,69 +9,109 @@ except ImportError:
|
|||||||
|
|
||||||
from .scaling_tracker import ScalingTracker
|
from .scaling_tracker import ScalingTracker
|
||||||
from ..font.ctk_font import CTkFont
|
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"):
|
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
|
ScalingTracker.add_widget(self._set_scaling, self) # add callback for automatic scaling changes
|
||||||
self._widget_scaling = ScalingTracker.get_widget_scaling(self)
|
self.__widget_scaling = ScalingTracker.get_widget_scaling(self)
|
||||||
elif self._scaling_type == "window":
|
elif self.__scaling_type == "window":
|
||||||
ScalingTracker.activate_high_dpi_awareness() # make process DPI aware
|
ScalingTracker.activate_high_dpi_awareness() # make process DPI aware
|
||||||
ScalingTracker.add_window(self._set_scaling, self) # add callback for automatic scaling changes
|
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):
|
def destroy(self):
|
||||||
if self._scaling_type == "widget":
|
if self.__scaling_type == "widget":
|
||||||
ScalingTracker.remove_widget(self._set_scaling, self)
|
ScalingTracker.remove_widget(self._set_scaling, self)
|
||||||
elif self._scaling_type == "window":
|
elif self.__scaling_type == "window":
|
||||||
ScalingTracker.remove_window(self._set_scaling, self)
|
ScalingTracker.remove_window(self._set_scaling, self)
|
||||||
|
|
||||||
def _apply_widget_scaling(self, value: Union[int, float, str]) -> Union[float, str]:
|
def _set_scaling(self, new_widget_scaling, new_window_scaling):
|
||||||
assert self._scaling_type == "widget"
|
""" 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)):
|
def _get_widget_scaling(self) -> float:
|
||||||
return value * self._widget_scaling
|
return self.__widget_scaling
|
||||||
else:
|
|
||||||
return value
|
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:
|
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 """
|
""" 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 type(font) == tuple:
|
||||||
if len(font) == 1:
|
if len(font) == 1:
|
||||||
return font
|
return font
|
||||||
elif len(font) == 2:
|
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:
|
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:
|
else:
|
||||||
raise ValueError(f"Can not scale font {font}. font needs to be tuple of len 1, 2 or 3")
|
raise ValueError(f"Can not scale font {font}. font needs to be tuple of len 1, 2 or 3")
|
||||||
|
|
||||||
elif isinstance(font, CTkFont):
|
elif isinstance(font, CTkFont):
|
||||||
return font.create_scaled_tuple(self._widget_scaling)
|
return font.create_scaled_tuple(self.__widget_scaling)
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Can not scale font '{font}' of type {type(font)}. font needs to be tuple or instance of CTkFont")
|
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:
|
def _apply_argument_scaling(self, kwargs: dict) -> dict:
|
||||||
assert self._scaling_type == "widget"
|
assert self.__scaling_type == "widget"
|
||||||
|
|
||||||
scaled_kwargs = copy.copy(kwargs)
|
scaled_kwargs = copy.copy(kwargs)
|
||||||
|
|
||||||
|
# scale padding values
|
||||||
if "pady" in scaled_kwargs:
|
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"])
|
scaled_kwargs["pady"] = self._apply_widget_scaling(scaled_kwargs["pady"])
|
||||||
elif isinstance(scaled_kwargs["pady"], tuple):
|
elif isinstance(scaled_kwargs["pady"], tuple):
|
||||||
scaled_kwargs["pady"] = tuple([self._apply_widget_scaling(v) for v in scaled_kwargs["pady"]])
|
scaled_kwargs["pady"] = tuple([self._apply_widget_scaling(v) for v in scaled_kwargs["pady"]])
|
||||||
if "padx" in kwargs:
|
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"])
|
scaled_kwargs["padx"] = self._apply_widget_scaling(scaled_kwargs["padx"])
|
||||||
elif isinstance(scaled_kwargs["padx"], tuple):
|
elif isinstance(scaled_kwargs["padx"], tuple):
|
||||||
scaled_kwargs["padx"] = tuple([self._apply_widget_scaling(v) for v in scaled_kwargs["padx"]])
|
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:
|
if "x" in scaled_kwargs:
|
||||||
scaled_kwargs["x"] = self._apply_widget_scaling(scaled_kwargs["x"])
|
scaled_kwargs["x"] = self._apply_widget_scaling(scaled_kwargs["x"])
|
||||||
if "y" in scaled_kwargs:
|
if "y" in scaled_kwargs:
|
||||||
@ -93,41 +133,29 @@ class CTkScalingBaseClass(ABC):
|
|||||||
return width, height, x, y
|
return width, height, x, y
|
||||||
|
|
||||||
def _apply_geometry_scaling(self, geometry_string: str) -> str:
|
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)
|
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
|
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
|
elif width is None and height is None: # no <width> and <height> in geometry_string
|
||||||
return f"+{x}+{y}"
|
return f"+{x}+{y}"
|
||||||
|
|
||||||
else:
|
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:
|
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)
|
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
|
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
|
elif width is None and height is None: # no <width> and <height> in geometry_string
|
||||||
return f"+{x}+{y}"
|
return f"+{x}+{y}"
|
||||||
|
|
||||||
else:
|
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 _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
|
|
||||||
|
@ -13,13 +13,13 @@ class App(customtkinter.CTk):
|
|||||||
|
|
||||||
self.title("CustomTkinter complex_example.py")
|
self.title("CustomTkinter complex_example.py")
|
||||||
self.geometry(f"{1100}x{580}")
|
self.geometry(f"{1100}x{580}")
|
||||||
self.minsize(800, 400)
|
#self.minsize(800, 400)
|
||||||
#self.maxsize(1200, 700)
|
#self.maxsize(1200, 700)
|
||||||
self.protocol("WM_DELETE_WINDOW", self.on_closing) # call .on_closing() when app gets closed
|
self.protocol("WM_DELETE_WINDOW", self.on_closing) # call .on_closing() when app gets closed
|
||||||
|
|
||||||
# configure grid layout (4x4)
|
# configure grid layout (4x4)
|
||||||
self.grid_columnconfigure(1, weight=1)
|
self.grid_columnconfigure(1, weight=1)
|
||||||
self.grid_columnconfigure((2, 3, 4), weight=0, minsize=200)
|
self.grid_columnconfigure((2, 3), weight=0)
|
||||||
self.grid_rowconfigure((0, 1, 2), weight=1)
|
self.grid_rowconfigure((0, 1, 2), weight=1)
|
||||||
|
|
||||||
# create sidebar frame with widgets
|
# create sidebar frame with widgets
|
||||||
@ -47,63 +47,18 @@ class App(customtkinter.CTk):
|
|||||||
|
|
||||||
# create main entry and button
|
# create main entry and button
|
||||||
self.entry = customtkinter.CTkEntry(self, placeholder_text="CTkEntry")
|
self.entry = customtkinter.CTkEntry(self, placeholder_text="CTkEntry")
|
||||||
self.entry.grid(row=3, column=1, columnspan=2, padx=(20, 10), pady=(10, 20), sticky="nsew")
|
self.entry.grid(row=3, column=1, columnspan=2, padx=(20, 0), pady=(20, 20), sticky="nsew")
|
||||||
|
|
||||||
self.main_button_1 = customtkinter.CTkButton(master=self, fg_color=None, border_width=2)
|
self.main_button_1 = customtkinter.CTkButton(master=self, fg_color=None, border_width=2)
|
||||||
self.main_button_1.grid(row=3, column=3, padx=(10, 20), pady=(10, 20), sticky="nsew")
|
self.main_button_1.grid(row=3, column=3, padx=(20, 20), pady=(20, 20), sticky="nsew")
|
||||||
|
|
||||||
# create textbox
|
# create textbox
|
||||||
self.textbox = customtkinter.CTkTextbox(self)
|
self.textbox = customtkinter.CTkTextbox(self, width=250)
|
||||||
self.textbox.grid(row=0, column=1, padx=(20, 10), pady=(20, 10), sticky="nsew")
|
self.textbox.grid(row=0, column=1, padx=(20, 0), pady=(20, 0), sticky="nsew")
|
||||||
|
|
||||||
# create radiobutton frame
|
|
||||||
self.radiobutton_frame = customtkinter.CTkFrame(self)
|
|
||||||
self.radiobutton_frame.grid(row=0, column=3, padx=(10, 10), pady=(20, 10), sticky="nsew")
|
|
||||||
self.radio_var = tkinter.IntVar(value=0)
|
|
||||||
self.label_radio_group = customtkinter.CTkLabel(master=self.radiobutton_frame, text="CTkRadioButton Group:")
|
|
||||||
self.label_radio_group.grid(row=0, column=2, columnspan=1, padx=10, pady=10, sticky="")
|
|
||||||
self.radio_button_1 = customtkinter.CTkRadioButton(master=self.radiobutton_frame, variable=self.radio_var, value=0)
|
|
||||||
self.radio_button_1.grid(row=1, column=2, pady=10, padx=20, sticky="n")
|
|
||||||
self.radio_button_2 = customtkinter.CTkRadioButton(master=self.radiobutton_frame, variable=self.radio_var, value=1)
|
|
||||||
self.radio_button_2.grid(row=2, column=2, pady=10, padx=20, sticky="n")
|
|
||||||
self.radio_button_3 = customtkinter.CTkRadioButton(master=self.radiobutton_frame, variable=self.radio_var, value=2)
|
|
||||||
self.radio_button_3.grid(row=3, column=2, pady=10, padx=20, sticky="n")
|
|
||||||
|
|
||||||
# create checkbox and switch frame
|
|
||||||
self.checkbox_slider_frame = customtkinter.CTkFrame(self)
|
|
||||||
self.checkbox_slider_frame.grid(row=0, column=4, padx=(10, 20), pady=(20, 10), sticky="nsew")
|
|
||||||
self.checkbox_1 = customtkinter.CTkCheckBox(master=self.checkbox_slider_frame)
|
|
||||||
self.checkbox_1.grid(row=1, column=0, pady=(20, 10), padx=20, sticky="n")
|
|
||||||
self.checkbox_2 = customtkinter.CTkCheckBox(master=self.checkbox_slider_frame)
|
|
||||||
self.checkbox_2.grid(row=2, column=0, pady=10, padx=20, sticky="n")
|
|
||||||
self.switch_1 = customtkinter.CTkSwitch(master=self.checkbox_slider_frame, command=lambda: print("switch 1 toggle"))
|
|
||||||
self.switch_1.grid(row=3, column=0, pady=10, padx=20, sticky="n")
|
|
||||||
self.switch_2 = customtkinter.CTkSwitch(master=self.checkbox_slider_frame)
|
|
||||||
self.switch_2.grid(row=4, column=0, pady=(10, 20), padx=20, sticky="n")
|
|
||||||
|
|
||||||
# create slider and progressbar frame
|
|
||||||
self.slider_progressbar_frame = customtkinter.CTkFrame(self, fg_color=None)
|
|
||||||
self.slider_progressbar_frame.grid(row=1, column=1, columnspan=2, padx=(20, 10), pady=(10, 10), sticky="nsew")
|
|
||||||
self.slider_progressbar_frame.grid_columnconfigure(0, weight=1)
|
|
||||||
self.slider_progressbar_frame.grid_rowconfigure(4, weight=1)
|
|
||||||
|
|
||||||
self.seg_button_1 = customtkinter.CTkSegmentedButton(self.slider_progressbar_frame)
|
|
||||||
self.seg_button_1.grid(row=0, column=0, padx=(20, 10), pady=(10, 10), sticky="ew")
|
|
||||||
|
|
||||||
self.progressbar_1 = customtkinter.CTkProgressBar(self.slider_progressbar_frame)
|
|
||||||
self.progressbar_1.grid(row=1, column=0, padx=(20, 10), pady=(10, 10), sticky="ew")
|
|
||||||
self.progressbar_2 = customtkinter.CTkProgressBar(self.slider_progressbar_frame)
|
|
||||||
self.progressbar_2.grid(row=2, column=0, padx=(20, 10), pady=(10, 10), sticky="ew")
|
|
||||||
self.slider_1 = customtkinter.CTkSlider(self.slider_progressbar_frame, from_=0, to=1, number_of_steps=4)
|
|
||||||
self.slider_1.grid(row=3, column=0, padx=(20, 10), pady=(10, 10), sticky="ew")
|
|
||||||
self.slider_2 = customtkinter.CTkSlider(self.slider_progressbar_frame, orientation="vertical")
|
|
||||||
self.slider_2.grid(row=0, column=1, rowspan=5, padx=(10, 10), pady=(10, 10), sticky="ns")
|
|
||||||
self.progressbar_3 = customtkinter.CTkProgressBar(self.slider_progressbar_frame, orientation="vertical")
|
|
||||||
self.progressbar_3.grid(row=0, column=2, rowspan=5, padx=(10, 20), pady=(10, 10), sticky="ns")
|
|
||||||
|
|
||||||
# create tabview
|
# create tabview
|
||||||
self.tabview = customtkinter.CTkTabview(self)
|
self.tabview = customtkinter.CTkTabview(self, width=250)
|
||||||
self.tabview.grid(row=1, column=3, columnspan=2, padx=(10, 20), pady=(10, 10), sticky="nsew")
|
self.tabview.grid(row=0, column=2, padx=(20, 0), pady=(20, 0), sticky="nsew")
|
||||||
self.tabview.add("CTkTabview")
|
self.tabview.add("CTkTabview")
|
||||||
self.tabview.add("Tab 2")
|
self.tabview.add("Tab 2")
|
||||||
self.tabview.add("Tab 3")
|
self.tabview.add("Tab 3")
|
||||||
@ -121,6 +76,51 @@ class App(customtkinter.CTk):
|
|||||||
command=self.open_input_dialog)
|
command=self.open_input_dialog)
|
||||||
self.string_input_button.grid(row=2, column=0, padx=20, pady=(10, 10))
|
self.string_input_button.grid(row=2, column=0, padx=20, pady=(10, 10))
|
||||||
|
|
||||||
|
# create radiobutton frame
|
||||||
|
self.radiobutton_frame = customtkinter.CTkFrame(self)
|
||||||
|
self.radiobutton_frame.grid(row=0, column=3, padx=(20, 20), pady=(20, 0), sticky="nsew")
|
||||||
|
self.radio_var = tkinter.IntVar(value=0)
|
||||||
|
self.label_radio_group = customtkinter.CTkLabel(master=self.radiobutton_frame, text="CTkRadioButton Group:")
|
||||||
|
self.label_radio_group.grid(row=0, column=2, columnspan=1, padx=10, pady=10, sticky="")
|
||||||
|
self.radio_button_1 = customtkinter.CTkRadioButton(master=self.radiobutton_frame, variable=self.radio_var, value=0)
|
||||||
|
self.radio_button_1.grid(row=1, column=2, pady=10, padx=20, sticky="n")
|
||||||
|
self.radio_button_2 = customtkinter.CTkRadioButton(master=self.radiobutton_frame, variable=self.radio_var, value=1)
|
||||||
|
self.radio_button_2.grid(row=2, column=2, pady=10, padx=20, sticky="n")
|
||||||
|
self.radio_button_3 = customtkinter.CTkRadioButton(master=self.radiobutton_frame, variable=self.radio_var, value=2)
|
||||||
|
self.radio_button_3.grid(row=3, column=2, pady=10, padx=20, sticky="n")
|
||||||
|
|
||||||
|
# create checkbox and switch frame
|
||||||
|
self.checkbox_slider_frame = customtkinter.CTkFrame(self)
|
||||||
|
self.checkbox_slider_frame.grid(row=1, column=3, padx=(20, 20), pady=(20, 0), sticky="nsew")
|
||||||
|
self.checkbox_1 = customtkinter.CTkCheckBox(master=self.checkbox_slider_frame)
|
||||||
|
self.checkbox_1.grid(row=1, column=0, pady=(20, 10), padx=20, sticky="n")
|
||||||
|
self.checkbox_2 = customtkinter.CTkCheckBox(master=self.checkbox_slider_frame)
|
||||||
|
self.checkbox_2.grid(row=2, column=0, pady=10, padx=20, sticky="n")
|
||||||
|
self.switch_1 = customtkinter.CTkSwitch(master=self.checkbox_slider_frame, command=lambda: print("switch 1 toggle"))
|
||||||
|
self.switch_1.grid(row=3, column=0, pady=10, padx=20, sticky="n")
|
||||||
|
self.switch_2 = customtkinter.CTkSwitch(master=self.checkbox_slider_frame)
|
||||||
|
self.switch_2.grid(row=4, column=0, pady=(10, 20), padx=20, sticky="n")
|
||||||
|
|
||||||
|
# create slider and progressbar frame
|
||||||
|
self.slider_progressbar_frame = customtkinter.CTkFrame(self, fg_color=None)
|
||||||
|
self.slider_progressbar_frame.grid(row=1, column=1, columnspan=2, padx=(20, 0), pady=(20, 0), sticky="nsew")
|
||||||
|
self.slider_progressbar_frame.grid_columnconfigure(0, weight=1)
|
||||||
|
self.slider_progressbar_frame.grid_rowconfigure(4, weight=1)
|
||||||
|
|
||||||
|
self.seg_button_1 = customtkinter.CTkSegmentedButton(self.slider_progressbar_frame)
|
||||||
|
self.seg_button_1.grid(row=0, column=0, padx=(20, 10), pady=(10, 10), sticky="ew")
|
||||||
|
|
||||||
|
self.progressbar_1 = customtkinter.CTkProgressBar(self.slider_progressbar_frame)
|
||||||
|
self.progressbar_1.grid(row=1, column=0, padx=(20, 10), pady=(10, 10), sticky="ew")
|
||||||
|
self.progressbar_2 = customtkinter.CTkProgressBar(self.slider_progressbar_frame)
|
||||||
|
self.progressbar_2.grid(row=2, column=0, padx=(20, 10), pady=(10, 10), sticky="ew")
|
||||||
|
self.slider_1 = customtkinter.CTkSlider(self.slider_progressbar_frame, from_=0, to=1, number_of_steps=4)
|
||||||
|
self.slider_1.grid(row=3, column=0, padx=(20, 10), pady=(10, 10), sticky="ew")
|
||||||
|
self.slider_2 = customtkinter.CTkSlider(self.slider_progressbar_frame, orientation="vertical")
|
||||||
|
self.slider_2.grid(row=0, column=1, rowspan=5, padx=(10, 10), pady=(10, 10), sticky="ns")
|
||||||
|
self.progressbar_3 = customtkinter.CTkProgressBar(self.slider_progressbar_frame, orientation="vertical")
|
||||||
|
self.progressbar_3.grid(row=0, column=2, rowspan=5, padx=(10, 20), pady=(10, 10), sticky="ns")
|
||||||
|
|
||||||
# set default values
|
# set default values
|
||||||
self.sidebar_button_3.configure(state="disabled", text="Disabled CTkButton")
|
self.sidebar_button_3.configure(state="disabled", text="Disabled CTkButton")
|
||||||
self.checkbox_2.configure(state="disabled")
|
self.checkbox_2.configure(state="disabled")
|
||||||
|
@ -1,77 +1,22 @@
|
|||||||
import PIL.ImageTk
|
|
||||||
|
|
||||||
import customtkinter
|
import customtkinter
|
||||||
import tkinter
|
|
||||||
from PIL import Image, ImageTk
|
from PIL import Image, ImageTk
|
||||||
import os
|
import os
|
||||||
|
|
||||||
PATH = os.path.dirname(os.path.realpath(__file__))
|
PATH = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
|
||||||
customtkinter.set_appearance_mode("System") # Modes: "System" (standard), "Dark", "Light"
|
|
||||||
customtkinter.set_default_color_theme("blue") # Themes: "blue" (standard), "green", "dark-blue"
|
|
||||||
|
|
||||||
|
app = customtkinter.CTk()
|
||||||
|
|
||||||
class App(customtkinter.CTk):
|
switch_1 = customtkinter.CTkSwitch(app, text="darkmode", command=lambda: customtkinter.set_appearance_mode("dark" if switch_1.get() == 1 else "light"))
|
||||||
def __init__(self):
|
switch_1.pack(padx=20, pady=20)
|
||||||
super().__init__()
|
|
||||||
self.geometry("450x260")
|
|
||||||
self.title("CustomTkinter example_button_images.py")
|
|
||||||
|
|
||||||
self.grid_rowconfigure(0, weight=1)
|
image_1 = customtkinter.CTkImage(light_image=Image.open(PATH + "/test_images/add_folder_dark.png"),
|
||||||
self.grid_columnconfigure(0, weight=1, minsize=200)
|
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"))
|
||||||
|
|
||||||
self.frame_1 = customtkinter.CTkFrame(master=self, width=250, height=240, corner_radius=15)
|
button_1 = customtkinter.CTkButton(app, image=image_1)
|
||||||
self.frame_1.grid(row=0, column=0, padx=20, pady=20, sticky="nsew")
|
button_1.pack(padx=20, pady=20)
|
||||||
self.frame_1.grid_columnconfigure(0, weight=1)
|
|
||||||
self.frame_1.grid_columnconfigure(1, weight=1)
|
|
||||||
|
|
||||||
self.settings_image = self.load_image("/test_images/settings.png", 20)
|
app.mainloop()
|
||||||
self.bell_image = self.load_image("/test_images/bell.png", 20)
|
|
||||||
self.add_folder_image = self.load_image("/test_images/add-folder.png", 20)
|
|
||||||
self.add_list_image = self.load_image("/test_images/add-folder.png", 20)
|
|
||||||
self.add_user_image = self.load_image("/test_images/add-user.png", 20)
|
|
||||||
self.chat_image = self.load_image("/test_images/chat.png", 20)
|
|
||||||
self.home_image = self.load_image("/test_images/home.png", 20)
|
|
||||||
|
|
||||||
self.button_1 = customtkinter.CTkButton(master=self.frame_1, image=self.settings_image, text="Add Folder", height=32,
|
|
||||||
compound="right", command=self.button_function)
|
|
||||||
self.button_1.grid(row=1, column=0, columnspan=2, padx=20, pady=(20, 10), sticky="ew")
|
|
||||||
|
|
||||||
self.button_2 = customtkinter.CTkButton(master=self.frame_1, image=self.add_list_image, text="Add Item", height=32,
|
|
||||||
compound="right", fg_color="#D35B58", hover_color="#C77C78",
|
|
||||||
command=self.button_function)
|
|
||||||
self.button_2.grid(row=2, column=0, columnspan=2, padx=20, pady=10, sticky="ew")
|
|
||||||
|
|
||||||
self.button_3 = customtkinter.CTkButton(master=self.frame_1, image=self.chat_image, text="", width=40, height=40,
|
|
||||||
corner_radius=10, fg_color="gray40", hover_color="gray25",
|
|
||||||
command=self.button_function)
|
|
||||||
self.button_3.grid(row=3, column=0, columnspan=1, padx=20, pady=10, sticky="w")
|
|
||||||
|
|
||||||
self.button_4 = customtkinter.CTkButton(master=self.frame_1, image=self.home_image, text="", width=40, height=40,
|
|
||||||
corner_radius=10, fg_color="gray40", hover_color="gray25",
|
|
||||||
command=self.button_function)
|
|
||||||
self.button_4.grid(row=3, column=1, columnspan=1, padx=20, pady=10, sticky="e")
|
|
||||||
|
|
||||||
self.button_5 = customtkinter.CTkButton(master=self, image=self.add_user_image, text="Add User", width=130, height=60, border_width=2,
|
|
||||||
corner_radius=10, compound="bottom", border_color="#D35B58", fg_color=("gray84", "gray25"),
|
|
||||||
hover_color="#C77C78", command=self.button_function)
|
|
||||||
self.button_5.grid(row=0, column=1, padx=20, pady=20)
|
|
||||||
|
|
||||||
self.scaling_button = customtkinter.CTkSegmentedButton(self, values=[0.8, 0.9, 1.0, 1.1, 1.2, 1.5],
|
|
||||||
command=lambda v: customtkinter.set_widget_scaling(v))
|
|
||||||
self.scaling_button.grid(row=1, column=0, pady=(0, 20))
|
|
||||||
self.mode_switch = customtkinter.CTkSwitch(self, text="darkmode", onvalue="dark", offvalue="light",
|
|
||||||
command=lambda: customtkinter.set_appearance_mode(self.mode_switch.get()))
|
|
||||||
self.mode_switch.grid(row=1, column=1, pady=(0, 20))
|
|
||||||
|
|
||||||
def load_image(self, path, image_size):
|
|
||||||
""" load rectangular image with path relative to PATH """
|
|
||||||
return ImageTk.PhotoImage(Image.open(PATH + path).resize((image_size, image_size)))
|
|
||||||
|
|
||||||
def button_function(self):
|
|
||||||
print("button pressed")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
app = App()
|
|
||||||
app.mainloop()
|
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 4.8 KiB |
@ -7,7 +7,7 @@ app.geometry("600x950")
|
|||||||
switch_1 = customtkinter.CTkSwitch(app, text="darkmode", command=lambda: customtkinter.set_appearance_mode("dark" if switch_1.get() == 1 else "light"))
|
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)
|
switch_1.pack(padx=20, pady=20)
|
||||||
|
|
||||||
seg_1 = customtkinter._CTkSegmentedButton(app, values=[])
|
seg_1 = customtkinter.CTkSegmentedButton(app, values=[])
|
||||||
seg_1.configure(values=["value 1", "Value 2", "Value 42", "Value 123", "longlonglong"])
|
seg_1.configure(values=["value 1", "Value 2", "Value 42", "Value 123", "longlonglong"])
|
||||||
seg_1.pack(padx=20, pady=20)
|
seg_1.pack(padx=20, pady=20)
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ frame_1.pack(padx=20, pady=20, fill="x")
|
|||||||
|
|
||||||
seg_2_var = customtkinter.StringVar(value="value 1")
|
seg_2_var = customtkinter.StringVar(value="value 1")
|
||||||
|
|
||||||
seg_2 = customtkinter._CTkSegmentedButton(frame_1, values=["value 1", "Value 2", "Value 42"], variable=seg_2_var)
|
seg_2 = customtkinter.CTkSegmentedButton(frame_1, values=["value 1", "Value 2", "Value 42"], variable=seg_2_var)
|
||||||
seg_2.configure(values=[])
|
seg_2.configure(values=[])
|
||||||
seg_2.configure(values=["value 1", "Value 2", "Value 42"])
|
seg_2.configure(values=["value 1", "Value 2", "Value 42"])
|
||||||
seg_2.pack(padx=20, pady=10)
|
seg_2.pack(padx=20, pady=10)
|
||||||
@ -32,14 +32,14 @@ frame_1_1.pack(padx=20, pady=10, fill="x")
|
|||||||
switch_2 = customtkinter.CTkSwitch(frame_1_1, text="change fg", command=lambda: frame_1_1.configure(fg_color="red" if switch_2.get() == 1 else "green"))
|
switch_2 = customtkinter.CTkSwitch(frame_1_1, text="change fg", command=lambda: frame_1_1.configure(fg_color="red" if switch_2.get() == 1 else "green"))
|
||||||
switch_2.pack(padx=20, pady=20)
|
switch_2.pack(padx=20, pady=20)
|
||||||
|
|
||||||
seg_3 = customtkinter._CTkSegmentedButton(frame_1_1, values=["value 1", "Value 2", "Value 42"])
|
seg_3 = customtkinter.CTkSegmentedButton(frame_1_1, values=["value 1", "Value 2", "Value 42"])
|
||||||
seg_3.pack(padx=20, pady=10)
|
seg_3.pack(padx=20, pady=10)
|
||||||
|
|
||||||
seg_4 = customtkinter._CTkSegmentedButton(app)
|
seg_4 = customtkinter.CTkSegmentedButton(app)
|
||||||
seg_4.pack(padx=20, pady=20)
|
seg_4.pack(padx=20, pady=20)
|
||||||
|
|
||||||
seg_5_var = customtkinter.StringVar(value="kfasjkfdklaj")
|
seg_5_var = customtkinter.StringVar(value="kfasjkfdklaj")
|
||||||
seg_5 = customtkinter._CTkSegmentedButton(app, corner_radius=1000, border_width=0, unselected_color="green",
|
seg_5 = customtkinter.CTkSegmentedButton(app, corner_radius=1000, border_width=0, unselected_color="green",
|
||||||
variable=seg_5_var)
|
variable=seg_5_var)
|
||||||
seg_5.pack(padx=20, pady=20)
|
seg_5.pack(padx=20, pady=20)
|
||||||
seg_5.configure(values=["1", "2", "3", "4"])
|
seg_5.configure(values=["1", "2", "3", "4"])
|
||||||
@ -56,7 +56,7 @@ label_seg_5 = customtkinter.CTkLabel(app, textvariable=seg_5_var)
|
|||||||
label_seg_5.pack(padx=20, pady=20)
|
label_seg_5.pack(padx=20, pady=20)
|
||||||
|
|
||||||
seg_6_var = customtkinter.StringVar(value="kfasjkfdklaj")
|
seg_6_var = customtkinter.StringVar(value="kfasjkfdklaj")
|
||||||
seg_6 = customtkinter._CTkSegmentedButton(app, width=300)
|
seg_6 = customtkinter.CTkSegmentedButton(app, width=300)
|
||||||
seg_6.pack(padx=20, pady=20)
|
seg_6.pack(padx=20, pady=20)
|
||||||
entry_6 = customtkinter.CTkEntry(app)
|
entry_6 = customtkinter.CTkEntry(app)
|
||||||
entry_6.pack(padx=20, pady=(0, 20))
|
entry_6.pack(padx=20, pady=(0, 20))
|
||||||
@ -70,7 +70,7 @@ label_6.pack(padx=20, pady=(0, 20))
|
|||||||
seg_6.configure(height=50, variable=seg_6_var)
|
seg_6.configure(height=50, variable=seg_6_var)
|
||||||
seg_6.delete("CTkSegmentedButton")
|
seg_6.delete("CTkSegmentedButton")
|
||||||
|
|
||||||
seg_7 = customtkinter._CTkSegmentedButton(app, values=["disabled seg button", "2", "3"])
|
seg_7 = customtkinter.CTkSegmentedButton(app, values=["disabled seg button", "2", "3"])
|
||||||
seg_7.pack(padx=20, pady=20)
|
seg_7.pack(padx=20, pady=20)
|
||||||
seg_7.configure(state="disabled")
|
seg_7.configure(state="disabled")
|
||||||
seg_7.set("2")
|
seg_7.set("2")
|
||||||
|
@ -2,7 +2,7 @@ import customtkinter
|
|||||||
|
|
||||||
app = customtkinter.CTk()
|
app = customtkinter.CTk()
|
||||||
|
|
||||||
tabview_1 = customtkinter._CTkTabview(app)
|
tabview_1 = customtkinter.CTkTabview(app)
|
||||||
tabview_1.pack(padx=20, pady=20)
|
tabview_1.pack(padx=20, pady=20)
|
||||||
|
|
||||||
tab_1 = tabview_1.add("tab 1")
|
tab_1 = tabview_1.add("tab 1")
|
||||||
|
Loading…
Reference in New Issue
Block a user