appearance_mode_tracker.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. import tkinter
  2. from typing import Callable
  3. #import darkdetect
  4. class AppearanceModeTracker:
  5. callback_list = []
  6. app_list = []
  7. update_loop_running = False
  8. update_loop_interval = 30 # milliseconds
  9. appearance_mode_set_by = "system"
  10. appearance_mode = 0 # Light (standard)
  11. @classmethod
  12. def init_appearance_mode(cls):
  13. if cls.appearance_mode_set_by == "system":
  14. new_appearance_mode = cls.detect_appearance_mode()
  15. if new_appearance_mode != cls.appearance_mode:
  16. cls.appearance_mode = new_appearance_mode
  17. cls.update_callbacks()
  18. @classmethod
  19. def add(cls, callback: Callable, widget=None):
  20. cls.callback_list.append(callback)
  21. if widget is not None:
  22. app = cls.get_tk_root_of_widget(widget)
  23. if app not in cls.app_list:
  24. cls.app_list.append(app)
  25. if not cls.update_loop_running:
  26. app.after(cls.update_loop_interval, cls.update)
  27. cls.update_loop_running = True
  28. @classmethod
  29. def remove(cls, callback: Callable):
  30. try:
  31. cls.callback_list.remove(callback)
  32. except ValueError:
  33. return
  34. @staticmethod
  35. def detect_appearance_mode() -> int:
  36. try:
  37. #if darkdetect.theme() == "Dark":
  38. return 0 # Dark
  39. #else:
  40. #return 0 # Light
  41. except NameError:
  42. return 0 # Light
  43. @classmethod
  44. def get_tk_root_of_widget(cls, widget):
  45. current_widget = widget
  46. while isinstance(current_widget, tkinter.Tk) is False:
  47. current_widget = current_widget.master
  48. return current_widget
  49. @classmethod
  50. def update_callbacks(cls):
  51. if cls.appearance_mode == 0:
  52. for callback in cls.callback_list:
  53. try:
  54. callback("Light")
  55. except Exception:
  56. continue
  57. elif cls.appearance_mode == 1:
  58. for callback in cls.callback_list:
  59. try:
  60. callback("Dark")
  61. except Exception:
  62. continue
  63. @classmethod
  64. def update(cls):
  65. if cls.appearance_mode_set_by == "system":
  66. new_appearance_mode = cls.detect_appearance_mode()
  67. if new_appearance_mode != cls.appearance_mode:
  68. cls.appearance_mode = new_appearance_mode
  69. cls.update_callbacks()
  70. # find an existing tkinter.Tk object for the next call of .after()
  71. for app in cls.app_list:
  72. try:
  73. app.after(cls.update_loop_interval, cls.update)
  74. return
  75. except Exception:
  76. continue
  77. cls.update_loop_running = False
  78. @classmethod
  79. def get_mode(cls) -> int:
  80. return cls.appearance_mode
  81. @classmethod
  82. def set_appearance_mode(cls, mode_string: str):
  83. if mode_string.lower() == "dark":
  84. cls.appearance_mode_set_by = "user"
  85. new_appearance_mode = 1
  86. if new_appearance_mode != cls.appearance_mode:
  87. cls.appearance_mode = new_appearance_mode
  88. cls.update_callbacks()
  89. elif mode_string.lower() == "light":
  90. cls.appearance_mode_set_by = "user"
  91. new_appearance_mode = 0
  92. if new_appearance_mode != cls.appearance_mode:
  93. cls.appearance_mode = new_appearance_mode
  94. cls.update_callbacks()
  95. elif mode_string.lower() == "system":
  96. cls.appearance_mode_set_by = "system"