Skip to content

Commit 2347b4c

Browse files
Add themes support
Added themes in plugins, added scaffolding for other users to add themes as well. Click buttons currently don't follow theme.
1 parent 511975b commit 2347b4c

File tree

6 files changed

+735
-24
lines changed

6 files changed

+735
-24
lines changed

blacs/plugins/theme/__init__.py

Lines changed: 286 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,22 @@
1212
#####################################################################
1313
import logging
1414
import os
15-
1615
from qtutils import *
17-
1816
from blacs.plugins import PLUGINS_DIR
1917

2018
name = "GUI Theme"
2119
module = "theme" # should be folder name
2220
logger = logging.getLogger('BLACS.plugin.%s'%module)
2321

22+
def load_theme(theme_name):
23+
theme_path = os.path.join(PLUGINS_DIR, module, "themes", f"{theme_name}_theme.qss")
24+
try:
25+
with open(theme_path, "r") as f:
26+
return f.read()
27+
except Exception as e:
28+
logger.warning(f"Could not load theme '{theme_name}': {e}")
29+
return ""
30+
2431

2532
DEFAULT_STYLESHEET = """DigitalOutput {
2633
font-size: 12px;
@@ -99,6 +106,252 @@
99106
}
100107
"""
101108

109+
# DARK_STYLESHEET = """
110+
# # /* ===== Global & Widget Defaults ===== */
111+
# # QWidget {
112+
# # background-color: #0f1115;
113+
# # color: #e6eef3;
114+
# # selection-background-color: #2b6ea3;
115+
# # selection-color: #ffffff;
116+
# # font-family: "Segoe UI", "Helvetica Neue", Arial, sans-serif;
117+
# # }
118+
119+
# # /* ===== Push Buttons ===== */
120+
# # QPushButton {
121+
# # background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1,
122+
# # stop:0 #2a2e34, stop:1 #212427);
123+
# # border: 1px solid #2e3338;
124+
# # padding: 6px 10px;
125+
# # border-radius: 6px;
126+
# # color: #e6eef3;
127+
# # }
128+
# # QPushButton:hover { border-color: #3a7bd5; }
129+
# # QPushButton:pressed { background-color: #1b1d20; }
130+
131+
# # /* ===== LineEdits, TextEdits ===== */
132+
# # QLineEdit, QPlainTextEdit, QTextEdit {
133+
# # background-color: #0c0d0f;
134+
# # border: 1px solid #26282c;
135+
# # padding: 6px;
136+
# # border-radius: 4px;
137+
# # color: #e6eef3;
138+
# # }
139+
# # QLineEdit:focus, QTextEdit:focus { border-color: #3a7bd5; }
140+
141+
# # /* ===== Tables, Headers ===== */
142+
# # QHeaderView::section {
143+
# # background-color: #131417;
144+
# # color: #cfd8de;
145+
# # padding: 6px;
146+
# # border: 1px solid #232427;
147+
# # }
148+
# # QTableView, QTreeView, QListView {
149+
# # background-color: #0b0c0e;
150+
# # gridline-color: #17181b;
151+
# # alternate-background-color: #0f1013;
152+
# # }
153+
# # QTableView::item:selected, QTreeView::item:selected, QListView::item:selected {
154+
# # background-color: #2b6ea3;
155+
# # color: #fff;
156+
# # }
157+
158+
# # /* ===== Combo & Spin Boxes ===== */
159+
# # QComboBox, QSpinBox, QDoubleSpinBox {
160+
# # background-color: #0c0d0f;
161+
# # border: 1px solid #26282c;
162+
# # padding: 4px;
163+
# # color: #e6eef3;
164+
# # }
165+
166+
# # /* ===== Checkboxes / Radios ===== */
167+
# # QCheckBox, QRadioButton { spacing: 6px; }
168+
# # QCheckBox::indicator, QRadioButton::indicator {
169+
# # background-color: transparent;
170+
# # border: 1px solid #3a3d42;
171+
# # }
172+
173+
# # /* ===== Progress Bar ===== */
174+
# # QProgressBar {
175+
# # border: 1px solid #2b2f34;
176+
# # background: #0c0d0f;
177+
# # text-align: center;
178+
# # border-radius: 6px;
179+
# # }
180+
# # QProgressBar::chunk {
181+
# # background-color: #3a7bd5;
182+
# # border-radius: 6px;
183+
# # }
184+
185+
# # /* ===== Tabs ===== */
186+
# # QTabWidget::pane { border: 1px solid #232427; }
187+
# # QTabBar::tab {
188+
# # background: #0f1115;
189+
# # color: #cfd8de;
190+
# # padding: 8px;
191+
# # border: 1px solid #232427;
192+
# # border-bottom: none;
193+
# # border-top-left-radius: 6px;
194+
# # border-top-right-radius: 6px;
195+
# # }
196+
# # QTabBar::tab:selected { background: #16181c; }
197+
198+
# # /* ===== Dock Widgets ===== */
199+
# # QDockWidget { titlebar-close-icon: url(:/icons/close.png); title: " "; }
200+
# # QDockWidget::title { background: #101215; }
201+
202+
# # /* ===== Menus & Tooltips ===== */
203+
# # QMenuBar, QMenu {
204+
# # background-color: #0f1115;
205+
# # color: #e6eef3;
206+
# # }
207+
# # QMenu::item:selected { background-color: #2b6ea3; color: #fff; }
208+
# # QToolTip {
209+
# # background-color: #1c1f22;
210+
# # color: #e6eef3;
211+
# # border: 1px solid #3a3f45;
212+
# # padding: 6px;
213+
# # }
214+
215+
# # /* ===== Scrollbars & Splitters ===== */
216+
# # QScrollBar:vertical {
217+
# # background: #0f1115;
218+
# # width: 12px;
219+
# # }
220+
# # QScrollBar::handle:vertical {
221+
# # background: #2b2e33;
222+
# # min-height: 20px;
223+
# # border-radius: 6px;
224+
# # }
225+
# # QScrollBar::add-line, QScrollBar::sub-line { height: 0px; }
226+
# # QSplitter::handle { background: #151618; }
227+
228+
# # /* ===== Status Bar ===== */
229+
# # QStatusBar { background: #0f1115; color: #cfd8de; }
230+
231+
# # """
232+
233+
# BLUE_STYLESHEET = """
234+
# /* ===== Global & Widget Defaults ===== */
235+
# QWidget {
236+
# background-color: #0a0f1a;
237+
# color: #e1e8f0;
238+
# selection-background-color: #1e4a72;
239+
# selection-color: #ffffff;
240+
# font-family: "Segoe UI", "Helvetica Neue", Arial, sans-serif;
241+
# }
242+
243+
# /* ===== Push Buttons ===== */
244+
# QPushButton {
245+
# background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1,
246+
# stop:0 #1a2332, stop:1 #0f1520);
247+
# border: 1px solid #1e2b3d;
248+
# padding: 6px 10px;
249+
# border-radius: 6px;
250+
# color: #e1e8f0;
251+
# }
252+
# QPushButton:hover { border-color: #2563eb; }
253+
# QPushButton:pressed { background-color: #0d1117; }
254+
255+
# /* ===== LineEdits, TextEdits ===== */
256+
# QLineEdit, QPlainTextEdit, QTextEdit {
257+
# background-color: #070b14;
258+
# border: 1px solid #1a2332;
259+
# padding: 6px;
260+
# border-radius: 4px;
261+
# color: #e1e8f0;
262+
# }
263+
# QLineEdit:focus, QTextEdit:focus { border-color: #2563eb; }
264+
265+
# /* ===== Tables, Headers ===== */
266+
# QHeaderView::section {
267+
# background-color: #0d1421;
268+
# color: #b8c5d6;
269+
# padding: 6px;
270+
# border: 1px solid #1a2332;
271+
# }
272+
# QTableView, QTreeView, QListView {
273+
# background-color: #060a12;
274+
# gridline-color: #0f1520;
275+
# alternate-background-color: #080d17;
276+
# }
277+
# QTableView::item:selected, QTreeView::item:selected, QListView::item:selected {
278+
# background-color: #1e4a72;
279+
# color: #fff;
280+
# }
281+
282+
# /* ===== Combo & Spin Boxes ===== */
283+
# QComboBox, QSpinBox, QDoubleSpinBox {
284+
# background-color: #070b14;
285+
# border: 1px solid #1a2332;
286+
# padding: 4px;
287+
# color: #e1e8f0;
288+
# }
289+
290+
# /* ===== Checkboxes / Radios ===== */
291+
# QCheckBox, QRadioButton { spacing: 6px; }
292+
# QCheckBox::indicator, QRadioButton::indicator {
293+
# background-color: transparent;
294+
# border: 1px solid #2c3e50;
295+
# }
296+
297+
# /* ===== Progress Bar ===== */
298+
# QProgressBar {
299+
# border: 1px solid #1a2332;
300+
# background: #070b14;
301+
# text-align: center;
302+
# border-radius: 6px;
303+
# }
304+
# QProgressBar::chunk {
305+
# background-color: #2563eb;
306+
# border-radius: 6px;
307+
# }
308+
309+
# /* ===== Tabs ===== */
310+
# QTabWidget::pane { border: 1px solid #1a2332; }
311+
# QTabBar::tab {
312+
# background: #0a0f1a;
313+
# color: #b8c5d6;
314+
# padding: 8px;
315+
# border: 1px solid #1a2332;
316+
# border-bottom: none;
317+
# border-top-left-radius: 6px;
318+
# border-top-right-radius: 6px;
319+
# }
320+
# QTabBar::tab:selected { background: #0f1520; }
321+
322+
# /* ===== Dock Widgets ===== */
323+
# QDockWidget { titlebar-close-icon: url(:/icons/close.png); title: " "; }
324+
# QDockWidget::title { background: #0a0f1a; }
325+
326+
# /* ===== Menus & Tooltips ===== */
327+
# QMenuBar, QMenu {
328+
# background-color: #0a0f1a;
329+
# color: #e1e8f0;
330+
# }
331+
# QMenu::item:selected { background-color: #1e4a72; color: #fff; }
332+
# QToolTip {
333+
# background-color: #0f1520;
334+
# color: #e1e8f0;
335+
# border: 1px solid #2c3e50;
336+
# padding: 6px;
337+
# }
338+
339+
# /* ===== Scrollbars & Splitters ===== */
340+
# QScrollBar:vertical {
341+
# background: #0a0f1a;
342+
# width: 12px;
343+
# }
344+
# QScrollBar::handle:vertical {
345+
# background: #1a2332;
346+
# min-height: 20px;
347+
# border-radius: 6px;
348+
# }
349+
# QScrollBar::add-line, QScrollBar::sub-line { height: 0px; }
350+
# QSplitter::handle { background: #0f1520; }
351+
352+
# /* ===== Status Bar ===== */
353+
# QStatusBar { background: #0a0f1a; color: #b8c5d6; }
354+
# """
102355

103356
def is_default_stylesheet(stylesheet):
104357
"""Return whether a stylesheet is the same as the default stylesheet, modulo whitespace"""
@@ -157,31 +410,49 @@ def close(self):
157410
class Setting(object):
158411
name = name
159412

160-
def __init__(self,data):
161-
# This is our data store!
413+
def __init__(self, data):
162414
self.data = data
163-
164415
if 'stylesheet' not in self.data or not self.data['stylesheet']:
165-
# If it's absent or an empty string, use the default stylesheet:
166-
self.data['stylesheet'] = DEFAULT_STYLESHEET
167-
168-
def on_set_green_button_theme(self):
169-
self.widgets['stylesheet'].appendPlainText(DEFAULT_STYLESHEET)
416+
self.data['stylesheet'] = load_theme('default') or DEFAULT_STYLESHEET
417+
418+
def on_set_default_theme(self):
419+
self.widgets['stylesheet'].setPlainText(load_theme('default'))
170420

171-
# Create the page, return the page and an icon to use on the label (the class name attribute will be used for the label text)
172-
def create_dialog(self,notebook):
421+
def on_set_green_button_theme(self):
422+
self.widgets['stylesheet'].setPlainText(DEFAULT_STYLESHEET)
423+
424+
def create_dialog(self, notebook):
173425
ui = UiLoader().load(os.path.join(PLUGINS_DIR, module, 'theme.ui'))
174-
175426
# restore current stylesheet
176427
ui.stylesheet_text.setPlainText(self.data['stylesheet'])
428+
# Populate theme_combo with available .qss files
429+
theme_dir = os.path.join(PLUGINS_DIR, module, 'themes')
430+
themes = [f for f in os.listdir(theme_dir) if f.endswith('.qss')]
431+
theme_names = [os.path.splitext(f)[0].replace('_theme','') for f in themes]
432+
ui.theme_combo.clear()
433+
ui.theme_combo.addItems(theme_names)
434+
# Select current theme if possible
435+
current_theme = None
436+
for name in theme_names:
437+
if load_theme(name) == self.data['stylesheet']:
438+
current_theme = name
439+
break
440+
if current_theme:
441+
idx = theme_names.index(current_theme)
442+
ui.theme_combo.setCurrentIndex(idx)
443+
# Handler for theme selection
444+
def on_theme_selected(idx):
445+
theme_name = theme_names[idx]
446+
ui.stylesheet_text.setPlainText(load_theme(theme_name))
447+
ui.theme_combo.currentIndexChanged.connect(on_theme_selected)
177448
ui.example_button.clicked.connect(self.on_set_green_button_theme)
178449

179450
# save reference to widget
180451
self.widgets = {}
181452
self.widgets['stylesheet'] = ui.stylesheet_text
182453
self.widgets['example_button'] = ui.example_button
183-
184-
return ui,None
454+
self.widgets['theme_combo'] = ui.theme_combo
455+
return ui, None
185456

186457
def get_value(self,name):
187458
if name in self.data:
@@ -206,5 +477,3 @@ def save(self):
206477

207478
def close(self):
208479
self.widgets['example_button'].clicked.disconnect(self.on_set_green_button_theme)
209-
210-

blacs/plugins/theme/theme.ui

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,20 @@
5454
<item>
5555
<widget class="QPlainTextEdit" name="stylesheet_text"/>
5656
</item>
57-
<item>
58-
<widget class="QPushButton" name="example_button">
59-
<property name="text">
60-
<string>Append example green digital out theme to custom stylesheet</string>
61-
</property>
62-
</widget>
63-
</item>
57+
<item>
58+
<widget class="QPushButton" name="example_button">
59+
<property name="text">
60+
<string>Append example green digital out theme to custom stylesheet</string>
61+
</property>
62+
</widget>
63+
</item>
64+
<item>
65+
<widget class="QComboBox" name="theme_combo">
66+
<property name="toolTip">
67+
<string>Select a theme to load its stylesheet</string>
68+
</property>
69+
</widget>
70+
</item>
6471
</layout>
6572
</widget>
6673
</widget>

0 commit comments

Comments
 (0)