CustomTkinter/customtkinter/appearance_mode_tracker.py

136 lines
4.0 KiB
Python
Raw Normal View History

2021-03-04 20:27:46 +03:00
import sys
import tkinter
from distutils.version import StrictVersion as Version
from typing import Callable
2021-03-04 20:27:46 +03:00
2022-05-02 00:29:14 +03:00
try:
import darkdetect
if Version(darkdetect.__version__) < Version("0.3.1"):
2022-05-22 00:41:29 +03:00
sys.stderr.write("WARNING: You have to upgrade the darkdetect library: pip3 install --upgrade darkdetect\n")
2022-05-02 00:29:14 +03:00
if sys.platform != "darwin":
exit()
2022-05-21 01:25:07 +03:00
except ImportError as err:
raise err
except Exception:
sys.stderr.write("customtkinter.appearance_mode_tracker warning: failed to import darkdetect")
2021-03-06 15:47:49 +03:00
2021-03-04 20:27:46 +03:00
class AppearanceModeTracker:
2021-03-04 20:27:46 +03:00
callback_list = []
app_list = []
update_loop_running = False
update_loop_interval = 500 # milliseconds
2021-03-04 20:27:46 +03:00
appearance_mode_set_by = "system"
appearance_mode = 0 # Light (standard)
2021-03-04 20:27:46 +03:00
@classmethod
def init_appearance_mode(cls):
if cls.appearance_mode_set_by == "system":
new_appearance_mode = cls.detect_appearance_mode()
2021-03-04 20:27:46 +03:00
if new_appearance_mode != cls.appearance_mode:
cls.appearance_mode = new_appearance_mode
cls.update_callbacks()
2021-03-04 20:27:46 +03:00
@classmethod
def add(cls, callback: Callable, widget=None):
cls.callback_list.append(callback)
2021-03-04 20:27:46 +03:00
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)
2021-03-04 20:27:46 +03:00
if not cls.update_loop_running:
app.after(cls.update_loop_interval, cls.update)
cls.update_loop_running = True
2021-03-04 20:27:46 +03:00
2022-02-23 00:38:40 +03:00
@classmethod
def remove(cls, callback: Callable):
try:
cls.callback_list.remove(callback)
except ValueError:
return
2022-02-23 00:38:40 +03:00
2021-03-04 20:27:46 +03:00
@staticmethod
def detect_appearance_mode() -> int:
try:
if darkdetect.theme() == "Dark":
return 1 # Dark
else:
2021-03-04 20:27:46 +03:00
return 0 # Light
except NameError:
2021-03-04 20:27:46 +03:00
return 0 # Light
2022-02-23 00:38:40 +03:00
@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
2021-03-04 20:27:46 +03:00
elif cls.appearance_mode == 1:
for callback in cls.callback_list:
try:
callback("Dark")
except Exception:
continue
2021-03-04 20:27:46 +03:00
@classmethod
def update(cls):
if cls.appearance_mode_set_by == "system":
new_appearance_mode = cls.detect_appearance_mode()
2021-03-04 20:27:46 +03:00
if new_appearance_mode != cls.appearance_mode:
cls.appearance_mode = new_appearance_mode
cls.update_callbacks()
2021-03-04 20:27:46 +03:00
# 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
2021-03-04 20:27:46 +03:00
cls.update_loop_running = False
2021-03-04 20:27:46 +03:00
@classmethod
def get_mode(cls) -> int:
2021-03-04 20:27:46 +03:00
return cls.appearance_mode
@classmethod
def set_appearance_mode(cls, mode_string: str):
2021-03-04 20:27:46 +03:00
if mode_string.lower() == "dark":
cls.appearance_mode_set_by = "user"
new_appearance_mode = 1
2021-03-04 20:27:46 +03:00
if new_appearance_mode != cls.appearance_mode:
cls.appearance_mode = new_appearance_mode
cls.update_callbacks()
2021-03-04 20:27:46 +03:00
elif mode_string.lower() == "light":
cls.appearance_mode_set_by = "user"
new_appearance_mode = 0
2021-03-04 20:27:46 +03:00
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"