Skip to content

Commit a6c7843

Browse files
author
PyCompiler ARK++
committed
feat: Add theme selection functionality with QComboBox and persistent settings
1 parent 6cf7398 commit a6c7843

3 files changed

Lines changed: 97 additions & 10 deletions

File tree

bcasl/only_mod/app.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from PySide6.QtWidgets import QApplication
99
from PySide6.QtGui import QIcon
10+
from PySide6.QtCore import QSettings
1011

1112
from .widgets.main_window import BCASLStudioWindow
1213

@@ -53,7 +54,20 @@ def run(argv: list[str]) -> int:
5354
except Exception:
5455
pass
5556

56-
_apply_theme(app)
57+
# Load saved theme if present, else apply default logic
58+
try:
59+
s = QSettings("BCASL", "BCASL Studio")
60+
theme_path = s.value("theme", type=str)
61+
if theme_path:
62+
try:
63+
qss = Path(theme_path).read_text(encoding="utf-8")
64+
app.setStyleSheet(qss)
65+
except Exception:
66+
_apply_theme(app)
67+
else:
68+
_apply_theme(app)
69+
except Exception:
70+
_apply_theme(app)
5771

5872
w = BCASLStudioWindow()
5973
w.show()

bcasl/only_mod/ui/studio.ui

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,12 @@
8080
<layout class="QHBoxLayout" name="layout_ws_buttons">
8181
<item><widget class="QPushButton" name="btn_load_ws"><property name="text"><string>Load Workspace</string></property></widget></item>
8282
<item><widget class="QPushButton" name="btn_save_cfg"><property name="text"><string>Save Config</string></property></widget></item>
83+
<item>
84+
<widget class="QComboBox" name="cb_theme">
85+
<property name="editable"><bool>false</bool></property>
86+
<property name="placeholderText"><string>Theme…</string></property>
87+
</widget>
88+
</item>
8389
<item><spacer name="sp_ws"><property name="orientation"><enum>Qt::Horizontal</enum></property><property name="sizeHint" stdset="0"><size><width>40</width><height>20</height></size></property></spacer></item>
8490
<item><widget class="QPushButton" name="btn_run"><property name="text"><string>Run</string></property></widget></item>
8591
</layout>

bcasl/only_mod/widgets/main_window.py

Lines changed: 76 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ def _load_ui_from_designer(self):
164164
self.filter_text = self.findChild(QLineEdit, "filter_text")
165165
self.filter_phase = self.findChild(QComboBox, "filter_phase")
166166
self.list_plugins = self.findChild(QListWidget, "list_plugins")
167+
self.cb_theme = self.findChild(QComboBox, "cb_theme")
167168
self.sp_timeout2 = self.findChild(QSpinBox, "sp_timeout2")
168169
self.chk_sandbox2 = self.findChild(QCheckBox, "chk_sandbox2")
169170
self.sp_parallel2 = self.findChild(QSpinBox, "sp_parallel2")
@@ -215,6 +216,13 @@ def _wire_logic(self):
215216
self.btn_run.clicked.connect(self._run)
216217
except Exception:
217218
pass
219+
# Theme combobox populate and wiring
220+
try:
221+
if self.cb_theme:
222+
self._populate_theme_combobox()
223+
self.cb_theme.currentIndexChanged.connect(self._apply_theme_from_combobox)
224+
except Exception:
225+
pass
218226
# Filters
219227
try:
220228
if self.filter_text:
@@ -257,12 +265,79 @@ def _apply_theme_file(self, path: Path):
257265
QApplication.instance().setStyleSheet(qss)
258266
except Exception:
259267
pass
268+
# Persist selected theme
269+
try:
270+
from PySide6.QtCore import QSettings
271+
s = QSettings("BCASL", "BCASL Studio")
272+
s.setValue("theme", str(path))
273+
except Exception:
274+
pass
275+
276+
def _clear_theme(self):
277+
try:
278+
from PySide6.QtWidgets import QApplication
279+
QApplication.instance().setStyleSheet("")
280+
except Exception:
281+
pass
282+
try:
283+
from PySide6.QtCore import QSettings
284+
QSettings("BCASL", "BCASL Studio").remove("theme")
285+
except Exception:
286+
pass
287+
288+
def _populate_theme_combobox(self):
289+
root = Path(__file__).resolve().parents[3]
290+
themes_dir = root / "themes"
291+
items = [("System (No theme)", "")] # (label, path)
292+
try:
293+
if themes_dir.is_dir():
294+
for qss in sorted(themes_dir.glob("*.qss")):
295+
items.append((qss.name, str(qss)))
296+
except Exception:
297+
pass
298+
try:
299+
self.cb_theme.blockSignals(True)
300+
self.cb_theme.clear()
301+
for label, path in items:
302+
self.cb_theme.addItem(label, path)
303+
# Set from saved settings
304+
from PySide6.QtCore import QSettings
305+
s = QSettings("BCASL", "BCASL Studio")
306+
saved = s.value("theme", "", type=str) or ""
307+
idx = 0
308+
for i in range(self.cb_theme.count()):
309+
if self.cb_theme.itemData(i) == saved:
310+
idx = i
311+
break
312+
self.cb_theme.setCurrentIndex(idx)
313+
finally:
314+
try:
315+
self.cb_theme.blockSignals(False)
316+
except Exception:
317+
pass
318+
319+
def _apply_theme_from_combobox(self):
320+
try:
321+
data = self.cb_theme.currentData()
322+
if not data:
323+
self._clear_theme()
324+
return
325+
self._apply_theme_file(Path(data))
326+
except Exception:
327+
pass
260328

261329
def _populate_theme_menu(self):
262330
# Scan themes/ at repo root
263331
if not getattr(self, "m_theme", None):
264332
return
265333
self.m_theme.clear()
334+
# Add system default/no theme option
335+
try:
336+
act_sys = self.m_theme.addAction("System (No theme)")
337+
act_sys.triggered.connect(lambda _=False: self._clear_theme())
338+
self.m_theme.addSeparator()
339+
except Exception:
340+
pass
266341
try:
267342
root = Path(__file__).resolve().parents[3]
268343
themes_dir = root / "themes"
@@ -319,15 +394,10 @@ def _settings_to_ui(self):
319394
except Exception:
320395
self.sp_timeout.setValue(0)
321396
self.chk_sandbox.setChecked(bool(opts.get("sandbox", True)))
322-
try:
323-
self.sp_parallel.setValue(int(opts.get("plugin_parallelism", 0)))
324-
except Exception:
325-
self.sp_parallel.setValue(0)
326-
# Mirror into compact controls
397+
# Mirror into compact controls
327398
try:
328399
self.sp_timeout2.setValue(int(float(opts.get("plugin_timeout_s", 0.0))))
329400
self.chk_sandbox2.setChecked(bool(opts.get("sandbox", True)))
330-
self.sp_parallel2.setValue(int(opts.get("plugin_parallelism", 0)))
331401
except Exception:
332402
pass
333403

@@ -338,11 +408,9 @@ def _ui_to_settings(self):
338408
if hasattr(self, "sp_timeout2"):
339409
self.cfg.options["plugin_timeout_s"] = float(self.sp_timeout2.value())
340410
self.cfg.options["sandbox"] = bool(self.chk_sandbox2.isChecked())
341-
self.cfg.options["plugin_parallelism"] = int(self.sp_parallel2.value())
342411
else:
343412
self.cfg.options["plugin_timeout_s"] = float(self.sp_timeout.value())
344413
self.cfg.options["sandbox"] = bool(self.chk_sandbox.isChecked())
345-
self.cfg.options["plugin_parallelism"] = int(self.sp_parallel.value())
346414

347415
def _populate_plugins(self):
348416
if not self.plugins_dir or not self.cfg:
@@ -431,7 +499,6 @@ def _sync_settings_from_compact(self):
431499
try:
432500
self.sp_timeout.setValue(int(self.sp_timeout2.value()))
433501
self.chk_sandbox.setChecked(bool(self.chk_sandbox2.isChecked()))
434-
self.sp_parallel.setValue(int(self.sp_parallel2.value()))
435502
except Exception:
436503
pass
437504

0 commit comments

Comments
 (0)