Skip to content

Commit 7c8a416

Browse files
committed
Add support for color theme in the "Settings" dialog box
1 parent 18980d1 commit 7c8a416

File tree

6 files changed

+62
-3
lines changed

6 files changed

+62
-3
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ See DataLab [roadmap page](https://datalab-platform.com/en/contributing/roadmap.
2525
* ROI editor:
2626
* Added toolbars for both Signal and Image ROI editors, to allow to zoom in and out, and to reset the zoom level easily
2727
* Rearranged the buttons in the ROI editor dialog box for better ergonomics and consistency with the Annotations editor ("View in a new window" dialog box)
28+
* Application color theme:
29+
* Added support for color theme (auto, light, dark) in the "Settings" dialog box
30+
* The color theme is applied without restarting the application
2831

2932
🛠️ Bug fixes:
3033

cdl/config.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ class MainSection(conf.Section, metaclass=conf.SectionMeta):
9494
Each class attribute is an option (metaclass is automatically affecting
9595
option names in .INI file based on class attribute names)."""
9696

97+
color_mode = conf.EnumOption(["auto", "dark", "light"])
9798
process_isolation_enabled = conf.Option()
9899
rpc_server_enabled = conf.Option()
99100
rpc_server_port = conf.Option()
@@ -282,6 +283,7 @@ def initialize():
282283
# options default values are set when used in the application code.
283284
#
284285
# Main section
286+
Conf.main.color_mode.get("auto")
285287
Conf.main.process_isolation_enabled.get(True)
286288
Conf.main.rpc_server_enabled.get(True)
287289
Conf.main.traceback_log_path.get(f".{APP_NAME}_traceback.log")

cdl/core/gui/main.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
from guidata.qthelpers import add_actions, create_action, win32_fix_title_bar_background
3333
from guidata.widgets.console import DockableConsole
3434
from plotpy.builder import make
35+
from plotpy.config import set_plotpy_dark_mode
3536
from plotpy.constants import PlotType
3637
from qtpy import QtCore as QC
3738
from qtpy import QtGui as QG
@@ -132,14 +133,11 @@ def __init__(self, console=None, hide_on_close=False):
132133
"""Initialize main window"""
133134
CDLMainWindow.__instance = self
134135
super().__init__()
135-
win32_fix_title_bar_background(self)
136136
self.setObjectName(APP_NAME)
137137
self.setWindowIcon(get_icon("DataLab.svg"))
138138

139139
execenv.log(self, "Starting initialization")
140140

141-
self.__restore_pos_and_size()
142-
143141
self.ready_flag = True
144142

145143
self.hide_on_close = hide_on_close
@@ -178,6 +176,8 @@ def __init__(self, console=None, hide_on_close=False):
178176
self.view_menu: QW.QMenu | None = None
179177
self.help_menu: QW.QMenu | None = None
180178

179+
self.__update_color_mode()
180+
181181
self.__is_modified = False
182182
self.set_modified(False)
183183

@@ -192,6 +192,7 @@ def __init__(self, console=None, hide_on_close=False):
192192
console = Conf.console.console_enabled.get()
193193
self.setup(console)
194194

195+
self.__restore_pos_and_size()
195196
execenv.log(self, "Initialization done")
196197

197198
# ------API related to XML-RPC remote control
@@ -1589,10 +1590,29 @@ def __about(self) -> None: # pragma: no cover
15891590
<p>{adv_conf}""",
15901591
)
15911592

1593+
def __update_color_mode(self) -> None:
1594+
"""Update color mode"""
1595+
color_mode = Conf.main.color_mode.get()
1596+
if color_mode != "auto":
1597+
set_plotpy_dark_mode(color_mode == "dark")
1598+
if self.docks is not None:
1599+
for dock in self.docks.values():
1600+
widget = dock.widget()
1601+
if isinstance(widget, DockablePlotWidget):
1602+
widget.update_color_mode()
1603+
if self.console is not None:
1604+
self.console.update_color_mode()
1605+
self.console.clear()
1606+
if self.macropanel is not None:
1607+
self.macropanel.update_color_mode()
1608+
win32_fix_title_bar_background(self)
1609+
15921610
def __edit_settings(self) -> None:
15931611
"""Edit settings"""
15941612
changed_options = edit_settings(self)
15951613
for option in changed_options:
1614+
if option == "color_mode":
1615+
self.__update_color_mode()
15961616
if option == "plot_toolbar_position":
15971617
for dock in self.docks.values():
15981618
widget = dock.widget()

cdl/core/gui/panel/macro.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,13 @@ def __init__(self, parent: QW.QWidget | None = None) -> None:
213213

214214
self.setup_actions()
215215

216+
def update_color_mode(self) -> None:
217+
"""Update color mode according to the current theme"""
218+
self.console.update_color_mode()
219+
self.console.clear()
220+
for macro in self.__macros:
221+
macro.editor.update_color_mode()
222+
216223
# ------AbstractPanel interface-----------------------------------------------------
217224
# pylint: disable=unused-argument
218225
def get_serializable_name(self, obj: Macro) -> str:

cdl/core/gui/settings.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ class MainSettings(gds.DataSet):
2626
"""DataLab main settings"""
2727

2828
g0 = gds.BeginGroup(_("Settings for main window and general features"))
29+
color_mode = gds.ChoiceItem(
30+
_("Color mode"),
31+
zip(Conf.main.color_mode.choices, Conf.main.color_mode.choices),
32+
help=_("Color mode for the application"),
33+
)
2934
process_isolation_enabled = gds.BoolItem(
3035
"",
3136
_("Process isolation"),
@@ -297,4 +302,5 @@ def edit_settings(parent: QW.QWidget) -> None:
297302
for vis_defaults in ("ima_defaults",):
298303
if getattr(paramdict["view"], vis_defaults):
299304
changed_options.append(vis_defaults)
305+
300306
return changed_options

cdl/utils/conf.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,27 @@ def remove(self):
9393
CONF.remove_option(self.section, self.option)
9494

9595

96+
class EnumOption(Option):
97+
"""Enum configuration option handler"""
98+
99+
def __init__(self, choices: list[str | int | float]) -> None:
100+
super().__init__()
101+
self.choices = choices
102+
103+
def get(self, default=NoDefault) -> str | int | float:
104+
"""Get configuration option value"""
105+
value = super().get(default)
106+
if value not in self.choices:
107+
raise ValueError(f"Invalid configuration option value {value}")
108+
return value
109+
110+
def set(self, value: str | int | float) -> None:
111+
"""Set configuration option value"""
112+
if value not in self.choices:
113+
raise ValueError(f"Invalid configuration option value {value}")
114+
super().set(value)
115+
116+
96117
class ConfigPathOption(Option):
97118
"""Configuration file path configuration option handler"""
98119

0 commit comments

Comments
 (0)