Skip to content

Commit

Permalink
gui/experiments: add custom color themes for experiment windows
Browse files Browse the repository at this point in the history
Signed-off-by: Florian Agbuya <[email protected]>
  • Loading branch information
fsagbuya committed Oct 18, 2024
1 parent 333623e commit 1cc9b26
Showing 1 changed file with 111 additions and 2 deletions.
113 changes: 111 additions & 2 deletions artiq/dashboard/experiments.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class _ArgumentEditor(EntryTreeWidget):
def __init__(self, manager, dock, expurl):
self.manager = manager
self.expurl = expurl
self.dock = dock

EntryTreeWidget.__init__(self)

Expand Down Expand Up @@ -78,6 +79,26 @@ async def _recompute_argument(self, name):
argument["desc"] = procdesc
argument["state"] = state
self.update_argument(name, argument)
self.reapply_color()

def reapply_color(self):
colors = self.dock.manager.get_colors(self.expurl)
palette = self.dock.palette()
self.apply_color(palette, colors["window"])

def apply_color(self, palette, color):
self.setPalette(palette)
for child in self.findChildren(QtWidgets.QWidget):
child.setPalette(palette)
child.setAutoFillBackground(True)
for i in range(self.topLevelItemCount()):
self._set_item_color(self.topLevelItem(i), color)

def _set_item_color(self, item, color):
for col in range(item.columnCount()):
item.setBackground(col, QtGui.QBrush() if color is None else QtGui.QColor(color))
for child_index in range(item.childCount()):
self._set_item_color(item.child(child_index), color)

# Hooks that allow user-supplied argument editors to react to imminent user
# actions. Here, we always keep the manager-stored submission arguments
Expand All @@ -92,6 +113,20 @@ def about_to_close(self):
log_levels = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]


class _ThemedTitleBar(QtWidgets.QProxyStyle):
def __init__(self, dock, style=None):
super().__init__(style)
self.dock = dock

def drawComplexControl(self, control, option, painter, widget=None):
if control == QtWidgets.QStyle.ComplexControl.CC_TitleBar:
option = QtWidgets.QStyleOptionTitleBar(option)
colors = self.dock.get_themed_colors()
option.palette.setColor(QtGui.QPalette.ColorRole.Window, colors["title_bar"])
option.palette.setColor(QtGui.QPalette.ColorRole.Highlight, colors["title_bar"])
self.baseStyle().drawComplexControl(control, option, painter, widget)


class _ExperimentDock(QtWidgets.QMdiSubWindow):
sigClosed = QtCore.pyqtSignal()

Expand Down Expand Up @@ -303,14 +338,63 @@ async def _recompute_arguments_task(self, overrides=dict()):
self.argeditor = editor_class(self.manager, self, self.expurl)
self.layout.addWidget(self.argeditor, 0, 0, 1, 5)
self.argeditor.restore_state(argeditor_state)
self.argeditor.reapply_color()

def contextMenuEvent(self, event):
menu = QtWidgets.QMenu(self)
select_title_bar_color = menu.addAction("Select title bar color")
select_window_color = menu.addAction("Select window color")
reset_colors = menu.addAction("Reset to default colors")
menu.addSeparator()
reset_sched = menu.addAction("Reset scheduler settings")
action = menu.exec(self.mapToGlobal(event.pos()))
if action == reset_sched:
if action == select_title_bar_color:
self.select_color("title_bar")
elif action == select_window_color:
self.select_color("window")
elif action == reset_colors:
self.reset_colors()
elif action == reset_sched:
asyncio.ensure_future(self._recompute_sched_options_task())

def select_color(self, key):
color = QtWidgets.QColorDialog.getColor(
title=f"Select {key.replace('_', ' ').title()} Color")
if color.isValid():
self.manager.set_color(self.expurl, key, color.name())
self.apply_colors()

def apply_colors(self):
colors = self.get_themed_colors()
palette = self.modify_palette(colors)
self.setPalette(palette)
self.setStyle(_ThemedTitleBar(self))
self.argeditor.apply_color(palette, colors["window"])

def modify_palette(self, colors):
palette = self.palette()
palette.setColor(QtGui.QPalette.ColorRole.Window, colors["window"])
palette.setColor(QtGui.QPalette.ColorRole.Base, colors["window"])
palette.setColor(QtGui.QPalette.ColorRole.Button, colors["window"])
palette.setColor(QtGui.QPalette.ColorRole.Text, colors["window_text"])
palette.setColor(QtGui.QPalette.ColorRole.ButtonText, colors["window_text"])
palette.setColor(QtGui.QPalette.ColorRole.WindowText, colors["window_text"])
return palette

def get_themed_colors(self):
colors = self.manager.get_colors(self.expurl)
themed_colors = {
"window": QtGui.QColor(colors["window"]),
"title_bar": QtGui.QColor(colors["title_bar"])
}
themed_colors["window_text"] = QtGui.QColor(
"#000000" if themed_colors["window"].lightness() > 128 else "#FFFFFF")
return themed_colors

def reset_colors(self):
self.manager.reset_colors(self.expurl)
self.apply_colors()

async def _recompute_sched_options_task(self):
try:
expdesc, _ = await self.manager.compute_expdesc(self.expurl)
Expand Down Expand Up @@ -457,6 +541,7 @@ def __init__(self, main_window, dataset_sub,
self.submission_options = dict()
self.submission_arguments = dict()
self.argument_ui_names = dict()
self.colors = dict()

self.datasets = dict()
dataset_sub.add_setmodel_callback(self.set_dataset_model)
Expand All @@ -483,6 +568,27 @@ def set_explist_model(self, model):
def set_schedule_model(self, model):
self.schedule = model.backing_store

def set_color(self, expurl, key, value):
if expurl not in self.colors:
self.colors[expurl] = {}
self.colors[expurl][key] = value

def get_colors(self, expurl):
if expurl in self.colors:
return self.colors[expurl]
else:
palette = QtWidgets.QApplication.palette()
colors = {
"window": palette.color(QtGui.QPalette.ColorRole.Window).name(),
"title_bar": palette.color(QtGui.QPalette.ColorRole.Highlight).name()
}
self.colors[expurl] = colors
return colors

def reset_colors(self, expurl):
if expurl in self.colors:
del self.colors[expurl]

def resolve_expurl(self, expurl):
if expurl[:5] == "repo:":
expinfo = self.explist[expurl[5:]]
Expand Down Expand Up @@ -592,6 +698,7 @@ def open_experiment(self, expurl):
self.open_experiments[expurl] = dock
dock.setAttribute(QtCore.Qt.WidgetAttribute.WA_DeleteOnClose)
self.main_window.centralWidget().addSubWindow(dock)
dock.apply_colors()
dock.show()
dock.sigClosed.connect(partial(self.on_dock_closed, expurl))
if expurl in self.dock_states:
Expand Down Expand Up @@ -708,7 +815,8 @@ def save_state(self):
"arguments": self.submission_arguments,
"docks": self.dock_states,
"argument_uis": self.argument_ui_names,
"open_docks": set(self.open_experiments.keys())
"open_docks": set(self.open_experiments.keys()),
"colors": self.colors
}

def restore_state(self, state):
Expand All @@ -719,6 +827,7 @@ def restore_state(self, state):
self.submission_options = state["options"]
self.submission_arguments = state["arguments"]
self.argument_ui_names = state.get("argument_uis", {})
self.colors = state.get("colors", {})
for expurl in state["open_docks"]:
self.open_experiment(expurl)

Expand Down

0 comments on commit 1cc9b26

Please sign in to comment.