2021-03-04 20:27:46 +03:00
|
|
|
from threading import Thread
|
|
|
|
from time import sleep
|
|
|
|
import sys
|
2021-07-13 12:23:34 +03:00
|
|
|
from distutils.version import StrictVersion as Version
|
|
|
|
import darkdetect
|
2021-03-04 20:27:46 +03:00
|
|
|
|
2021-07-13 12:23:34 +03:00
|
|
|
if Version(darkdetect.__version__) < Version("0.3.1"):
|
|
|
|
sys.stderr.write("WARNING: You have to update the darkdetect library: pip3 install --upgrade darkdetect\n")
|
|
|
|
if sys.platform != "darwin":
|
|
|
|
exit()
|
2021-03-06 15:47:49 +03:00
|
|
|
|
2021-03-04 20:27:46 +03:00
|
|
|
|
|
|
|
class SystemAppearanceModeListener(Thread):
|
|
|
|
""" This class checks for a system appearance change
|
|
|
|
in a loop, and if a change is detected, than the
|
|
|
|
callback function gets called. Either 'Light' or
|
|
|
|
'Dark' is passed in the callback function. """
|
|
|
|
|
|
|
|
def __init__(self, callback, *args, **kwargs):
|
|
|
|
super().__init__(*args, **kwargs)
|
|
|
|
self.setDaemon(True)
|
|
|
|
|
|
|
|
self.appearance_mode = self.detect_appearance_mode()
|
|
|
|
self.callback_function = callback
|
|
|
|
|
|
|
|
self.activated = True
|
|
|
|
|
|
|
|
def activate(self):
|
|
|
|
self.activated = True
|
|
|
|
|
|
|
|
def deactivate(self):
|
|
|
|
self.activated = False
|
|
|
|
|
|
|
|
def get_mode(self):
|
|
|
|
return self.appearance_mode
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def detect_appearance_mode():
|
2021-07-13 12:23:34 +03:00
|
|
|
try:
|
|
|
|
if darkdetect.theme() == "Dark":
|
|
|
|
return 1 # Dark
|
|
|
|
else:
|
2021-03-04 20:27:46 +03:00
|
|
|
return 0 # Light
|
2021-07-13 12:23:34 +03:00
|
|
|
except NameError:
|
2021-03-04 20:27:46 +03:00
|
|
|
return 0 # Light
|
|
|
|
|
|
|
|
def run(self):
|
|
|
|
while True:
|
|
|
|
if self.activated:
|
|
|
|
detected_mode = self.detect_appearance_mode()
|
|
|
|
if detected_mode != self.appearance_mode:
|
|
|
|
self.appearance_mode = detected_mode
|
|
|
|
|
|
|
|
if self.appearance_mode == 0:
|
|
|
|
self.callback_function("Light", from_listener=True)
|
|
|
|
else:
|
|
|
|
self.callback_function("Dark", from_listener=True)
|
|
|
|
sleep(0.5)
|
|
|
|
|
|
|
|
|
2021-09-14 15:48:17 +03:00
|
|
|
class SystemAppearanceModeListenerNoThread:
|
2021-03-04 20:27:46 +03:00
|
|
|
def __init__(self, callback):
|
|
|
|
self.appearance_mode = self.detect_appearance_mode()
|
|
|
|
self.callback_function = callback
|
|
|
|
|
|
|
|
self.activated = True
|
|
|
|
|
|
|
|
def get_mode(self):
|
|
|
|
return self.appearance_mode
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def detect_appearance_mode():
|
2021-07-13 12:23:34 +03:00
|
|
|
try:
|
|
|
|
if darkdetect.theme() == "Dark":
|
|
|
|
return 1 # Dark
|
|
|
|
else:
|
2021-03-04 20:27:46 +03:00
|
|
|
return 0 # Light
|
2021-07-13 12:23:34 +03:00
|
|
|
except NameError:
|
2021-03-04 20:27:46 +03:00
|
|
|
return 0 # Light
|
|
|
|
|
|
|
|
def update(self):
|
|
|
|
detected_mode = self.detect_appearance_mode()
|
|
|
|
if detected_mode != self.appearance_mode:
|
|
|
|
self.appearance_mode = detected_mode
|
|
|
|
|
|
|
|
if self.appearance_mode == 0:
|
|
|
|
self.callback_function("Light", from_listener=True)
|
|
|
|
else:
|
|
|
|
self.callback_function("Dark", from_listener=True)
|
|
|
|
|
|
|
|
|
|
|
|
class AppearanceModeTracker():
|
|
|
|
""" This class holds a list with callback functions
|
|
|
|
of every customtkinter object that gets created.
|
|
|
|
And when either the SystemAppearanceModeListener
|
|
|
|
or the user changes the appearance_mode, all
|
|
|
|
callbacks in the list get called and the
|
|
|
|
new appearance_mode is passed over to the
|
|
|
|
customtkinter objects """
|
|
|
|
|
|
|
|
callback_list = []
|
|
|
|
appearance_mode = 0 # Light (standard)
|
|
|
|
system_mode_listener = None
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def init_listener_function(cls, no_thread=False):
|
|
|
|
if isinstance(cls.system_mode_listener, SystemAppearanceModeListener):
|
|
|
|
cls.system_mode_listener.deactivate()
|
|
|
|
|
|
|
|
if no_thread is True:
|
|
|
|
cls.system_mode_listener = SystemAppearanceModeListenerNoThread(cls.set_appearance_mode)
|
|
|
|
cls.appearance_mode = cls.system_mode_listener.get_mode()
|
|
|
|
else:
|
|
|
|
cls.system_mode_listener = SystemAppearanceModeListener(cls.set_appearance_mode)
|
|
|
|
cls.system_mode_listener.start()
|
|
|
|
cls.appearance_mode = cls.system_mode_listener.get_mode()
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def add(cls, callback):
|
|
|
|
cls.callback_list.append(callback)
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def get_mode(cls):
|
|
|
|
return cls.appearance_mode
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def set_appearance_mode(cls, mode_string, from_listener=False):
|
|
|
|
if mode_string.lower() == "dark":
|
|
|
|
cls.appearance_mode = 1
|
|
|
|
|
|
|
|
if not from_listener:
|
|
|
|
cls.system_mode_listener.deactivate()
|
|
|
|
|
|
|
|
elif mode_string.lower() == "light":
|
|
|
|
cls.appearance_mode = 0
|
|
|
|
if not from_listener:
|
|
|
|
cls.system_mode_listener.deactivate()
|
|
|
|
|
|
|
|
elif mode_string.lower() == "system":
|
|
|
|
cls.system_mode_listener.activate()
|
|
|
|
|
|
|
|
if cls.appearance_mode == 0:
|
|
|
|
for callback in cls.callback_list:
|
|
|
|
callback("Light")
|
|
|
|
elif cls.appearance_mode == 1:
|
|
|
|
for callback in cls.callback_list:
|
|
|
|
callback("Dark")
|
|
|
|
|
|
|
|
|
|
|
|
AppearanceModeTracker.init_listener_function()
|