Umstellung auf Customtkinter.
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
from .appearance_mode_base_class import CTkAppearanceModeBaseClass
|
||||
from .appearance_mode_tracker import AppearanceModeTracker
|
||||
|
||||
AppearanceModeTracker.init_appearance_mode()
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,61 @@
|
||||
from typing import Union, Tuple, List
|
||||
|
||||
from .appearance_mode_tracker import AppearanceModeTracker
|
||||
|
||||
|
||||
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() to convert tuple color
|
||||
|
||||
"""
|
||||
def __init__(self):
|
||||
AppearanceModeTracker.add(self._set_appearance_mode, self)
|
||||
self.__appearance_mode = AppearanceModeTracker.get_mode() # 0: "Light" 1: "Dark"
|
||||
|
||||
def destroy(self):
|
||||
AppearanceModeTracker.remove(self._set_appearance_mode)
|
||||
|
||||
def _set_appearance_mode(self, mode_string: str):
|
||||
""" can be overridden but super method must be called at the beginning """
|
||||
if mode_string.lower() == "dark":
|
||||
self.__appearance_mode = 1
|
||||
elif mode_string.lower() == "light":
|
||||
self.__appearance_mode = 0
|
||||
|
||||
def _get_appearance_mode(self) -> str:
|
||||
""" 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:
|
||||
return color
|
||||
|
||||
@staticmethod
|
||||
def _check_color_type(color: any, transparency: bool = False):
|
||||
if color is None:
|
||||
raise ValueError(f"color is None, for transparency set color='transparent'")
|
||||
elif isinstance(color, (tuple, list)) and (color[0] == "transparent" or color[1] == "transparent"):
|
||||
raise ValueError(f"transparency is not allowed in tuple color {color}, use 'transparent'")
|
||||
elif color == "transparent" and transparency is False:
|
||||
raise ValueError(f"transparency is not allowed for this attribute")
|
||||
elif isinstance(color, str):
|
||||
return color
|
||||
elif isinstance(color, (tuple, list)) and len(color) == 2 and isinstance(color[0], str) and isinstance(color[1], str):
|
||||
return color
|
||||
else:
|
||||
raise ValueError(f"color {color} must be string ('transparent' or 'color-name' or 'hex-color') or tuple of two strings, not {type(color)}")
|
||||
@@ -0,0 +1,122 @@
|
||||
import tkinter
|
||||
from typing import Callable
|
||||
import darkdetect
|
||||
|
||||
|
||||
class AppearanceModeTracker:
|
||||
|
||||
callback_list = []
|
||||
app_list = []
|
||||
update_loop_running = False
|
||||
update_loop_interval = 30 # milliseconds
|
||||
|
||||
appearance_mode_set_by = "system"
|
||||
appearance_mode = 0 # Light (standard)
|
||||
|
||||
@classmethod
|
||||
def init_appearance_mode(cls):
|
||||
if cls.appearance_mode_set_by == "system":
|
||||
new_appearance_mode = cls.detect_appearance_mode()
|
||||
|
||||
if new_appearance_mode != cls.appearance_mode:
|
||||
cls.appearance_mode = new_appearance_mode
|
||||
cls.update_callbacks()
|
||||
|
||||
@classmethod
|
||||
def add(cls, callback: Callable, widget=None):
|
||||
cls.callback_list.append(callback)
|
||||
|
||||
if widget is not None:
|
||||
app = cls.get_tk_root_of_widget(widget)
|
||||
if app not in cls.app_list:
|
||||
cls.app_list.append(app)
|
||||
|
||||
if not cls.update_loop_running:
|
||||
app.after(cls.update_loop_interval, cls.update)
|
||||
cls.update_loop_running = True
|
||||
|
||||
@classmethod
|
||||
def remove(cls, callback: Callable):
|
||||
try:
|
||||
cls.callback_list.remove(callback)
|
||||
except ValueError:
|
||||
return
|
||||
|
||||
@staticmethod
|
||||
def detect_appearance_mode() -> int:
|
||||
try:
|
||||
if darkdetect.theme() == "Dark":
|
||||
return 1 # Dark
|
||||
else:
|
||||
return 0 # Light
|
||||
except NameError:
|
||||
return 0 # Light
|
||||
|
||||
@classmethod
|
||||
def get_tk_root_of_widget(cls, widget):
|
||||
current_widget = widget
|
||||
|
||||
while isinstance(current_widget, tkinter.Tk) is False:
|
||||
current_widget = current_widget.master
|
||||
|
||||
return current_widget
|
||||
|
||||
@classmethod
|
||||
def update_callbacks(cls):
|
||||
if cls.appearance_mode == 0:
|
||||
for callback in cls.callback_list:
|
||||
try:
|
||||
callback("Light")
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
elif cls.appearance_mode == 1:
|
||||
for callback in cls.callback_list:
|
||||
try:
|
||||
callback("Dark")
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
@classmethod
|
||||
def update(cls):
|
||||
if cls.appearance_mode_set_by == "system":
|
||||
new_appearance_mode = cls.detect_appearance_mode()
|
||||
|
||||
if new_appearance_mode != cls.appearance_mode:
|
||||
cls.appearance_mode = new_appearance_mode
|
||||
cls.update_callbacks()
|
||||
|
||||
# find an existing tkinter.Tk object for the next call of .after()
|
||||
for app in cls.app_list:
|
||||
try:
|
||||
app.after(cls.update_loop_interval, cls.update)
|
||||
return
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
cls.update_loop_running = False
|
||||
|
||||
@classmethod
|
||||
def get_mode(cls) -> int:
|
||||
return cls.appearance_mode
|
||||
|
||||
@classmethod
|
||||
def set_appearance_mode(cls, mode_string: str):
|
||||
if mode_string.lower() == "dark":
|
||||
cls.appearance_mode_set_by = "user"
|
||||
new_appearance_mode = 1
|
||||
|
||||
if new_appearance_mode != cls.appearance_mode:
|
||||
cls.appearance_mode = new_appearance_mode
|
||||
cls.update_callbacks()
|
||||
|
||||
elif mode_string.lower() == "light":
|
||||
cls.appearance_mode_set_by = "user"
|
||||
new_appearance_mode = 0
|
||||
|
||||
if new_appearance_mode != cls.appearance_mode:
|
||||
cls.appearance_mode = new_appearance_mode
|
||||
cls.update_callbacks()
|
||||
|
||||
elif mode_string.lower() == "system":
|
||||
cls.appearance_mode_set_by = "system"
|
||||
Reference in New Issue
Block a user