dark mode toggle + enhancements

This commit is contained in:
2026-08-27 17:50:23 -07:00
parent 83ab73a05a
commit a9ea0efcb4
5 changed files with 211 additions and 53 deletions
+1 -2
View File
@@ -831,11 +831,10 @@ class ParticipantFoldChannelsWidget(FlaresBaseWidget):
self.scroll_area.setWidgetResizable(True)
self.scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
self.scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
self.scroll_area.setStyleSheet("QScrollArea { border: none; background-color: #f1f3f5; }")
self.scroll_area.setStyleSheet("QScrollArea { border: none; }")
# 2. Create the central canvas widget that inside the scroll block
self.scroll_content_widget = QWidget()
self.scroll_content_widget.setStyleSheet("background-color: #f1f3f5;")
# 3. Establish the strict 3-column layout grid engine
self.grid_layout = QGridLayout(self.scroll_content_widget)
+17 -5
View File
@@ -14,7 +14,7 @@ import pandas as pd
from pandas import DataFrame
from PySide6.QtWidgets import QApplication, QComboBox, QDialog, QGridLayout, QHBoxLayout, QLabel, QLineEdit, QListView, QMessageBox, QPushButton, QScrollArea, QSizePolicy, QVBoxLayout, QWidget, QFrame, QSpinBox, QFileDialog
from PySide6.QtGui import QStandardItemModel, QStandardItem, QPixmap, QIntValidator, QDoubleValidator
from PySide6.QtGui import QPalette, QStandardItemModel, QStandardItem, QPixmap, QIntValidator, QDoubleValidator
from PySide6.QtCore import QEvent, QPoint, QSize, QTimer, Qt, Signal
from src.shared.shareddata import APP_NAME, PIPELINE_STAGES
@@ -323,10 +323,10 @@ class ParamSection(QWidget):
self._updating_checkstates = False
# Title label
title_label = QLabel(section_data["title"])
title_label.setStyleSheet("font-weight: bold; font-size: 14px; margin-top: 10px; margin-bottom: 5px;")
layout.addWidget(title_label)
self.header_widgets.append(title_label)
self.title_label = QLabel(section_data["title"])
self.title_label.setStyleSheet("font-weight: bold; font-size: 14px; margin-top: 10px; margin-bottom: 5px;")
layout.addWidget(self.title_label)
self.header_widgets.append(self.title_label)
# Horizontal line
line = QFrame()
@@ -444,6 +444,10 @@ class ParamSection(QWidget):
self.update_dependencies()
def update_theme_colors(self):
self.title_label.style().unpolish(self.title_label)
self.title_label.style().polish(self.title_label)
def is_different(self, val_a, val_b, param_type=None):
"""Compares two parameter values to determine if they differ."""
type_str = str(param_type).lower()
@@ -890,6 +894,7 @@ class ProgressBubble(QWidget):
border-radius: 10px;
padding: 8px 12px;
background-color: #e0f0ff;
color: #000000;
}
""")
@@ -985,6 +990,13 @@ class ProgressBubble(QWidget):
self.spinner_idx += 1
self._update_label_text()
def reset(self):
"""Resets the bubble's visual state, progress bars, timers, and labels back to initial state."""
# Reset progress metrics and visual rectangles back to white
self.current_step = 0
for rect in self.rects:
rect.setStyleSheet("background-color: white; border: 1px solid gray;")
class FlaresBaseWidget(QWidget):
def __init__(self, caller):
+74 -28
View File
@@ -34,7 +34,9 @@ class UpdateOptodesWindow(QWidget):
self.setWindowTitle(f"Update optode positions - {APP_NAME.upper()}")
self.resize(760, 200)
self.label_file_a = QLabel("SNIRF file:")
self.selected_snirf_files: list[str] = []
self.label_file_a = QLabel("SNIRF files:")
self.line_edit_file_a = QLineEdit()
self.line_edit_file_a.setReadOnly(True)
self.btn_browse_a = QPushButton("Browse .snirf")
@@ -171,9 +173,16 @@ class UpdateOptodesWindow(QWidget):
msg.exec()
def browse_file_a(self) -> None:
file_path, _ = QFileDialog.getOpenFileName(self, "Select SNIRF File", "", "SNIRF Files (*.snirf)")
if file_path:
self.line_edit_file_a.setText(file_path)
file_paths, _ = QFileDialog.getOpenFileNames(
self,
"Select SNIRF Files",
"",
"SNIRF Files (*.snirf)"
)
if file_paths:
self.selected_snirf_files = file_paths
self.line_edit_file_a.setText("; ".join(Path(p).name for p in file_paths))
def browse_file_b(self) -> None:
file_path, _ = QFileDialog.getOpenFileName(self, "Select File", "", "Supported Files (*.txt *.xlsx)")
@@ -181,6 +190,7 @@ class UpdateOptodesWindow(QWidget):
self.line_edit_file_b.setText(file_path)
def clear_files(self) -> None:
self.selected_snirf_files.clear()
self.line_edit_file_a.clear()
self.line_edit_file_b.clear()
@@ -189,40 +199,76 @@ class UpdateOptodesWindow(QWidget):
file_b = self.line_edit_file_b.text()
suffix = self.line_edit_suffix.text().strip() or "flare"
if not file_a:
QMessageBox.critical(self, "Missing File", "Please select a SNIRF file.")
if not self.selected_snirf_files:
QMessageBox.critical(self, "Missing File", "Please select at least one SNIRF file.")
return
if not file_b:
QMessageBox.critical(self, "Missing File", "Please select a TXT file.")
QMessageBox.critical(self, "Missing File", "Please select a TXT or XLSX digitization file.")
return
# Get original filename without extension
base_name = os.path.splitext(os.path.basename(file_a))[0]
suggested_name = f"{base_name}_{suffix}.snirf"
# Open save dialog with default name
save_path, _ = QFileDialog.getSaveFileName(
output_dir = QFileDialog.getExistingDirectory(
self,
"Save SNIRF File As",
suggested_name,
"SNIRF Files (*.snirf)"
"Select Output Directory"
)
if not save_path:
if not output_dir:
print("Save cancelled.")
return
# Ensure .snirf extension
if not save_path.lower().endswith(".snirf"):
save_path += ".snirf"
output_path = Path(output_dir)
try:
self.update_optode_positions(file_a=file_a, file_b=file_b, save_path=save_path)
except Exception as e:
QMessageBox.critical(self, "Error", f"Failed to write file:\n{e}")
return
QMessageBox.information(self, "File Saved", f"File was saved to:\n{save_path}")
successful_files: list[str] = []
failed_files: list[str] = []
for file_a in self.selected_snirf_files:
input_path = Path(file_a)
# Keep original filename and independently add suffix
save_path = output_path / f"{input_path.stem}_{suffix}.snirf"
try:
self.update_optode_positions(
file_a=file_a,
file_b=file_b,
save_path=save_path
)
successful_files.append(save_path.name)
except Exception as e:
failed_files.append(
f"{input_path.name}: {e}"
)
# Build summary
message_parts: list[str] = []
if successful_files:
message_parts.append(
f"Successfully processed {len(successful_files)} "
f"SNIRF file(s):\n\n"
+ "\n".join(successful_files)
)
if failed_files:
message_parts.append(
f"Failed to process {len(failed_files)} "
f"SNIRF file(s):\n\n"
+ "\n".join(failed_files)
)
if failed_files:
QMessageBox.warning(
self,
"Processing Complete",
"\n\n".join(message_parts)
)
else:
QMessageBox.information(
self,
"Files Saved",
"\n\n".join(message_parts)
)
def update_optode_positions(
self,
@@ -306,6 +352,6 @@ class UpdateOptodesWindow(QWidget):
initial_montage = make_dig_montage(ch_pos=ch_positions, nasion=fiducials.get('nz'), lpa=fiducials.get('lpa'), rpa=fiducials.get('rpa'), coord_frame='head') # type: ignore
# Read the SNIRF file, set the montage, and write it back
raw = read_raw_snirf(file_a, preload=True)
raw = read_raw_snirf(str(file_a), preload=True)
raw.set_montage(initial_montage) # type: ignore
write_raw_snirf(raw, save_path)