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
+5 -2
View File
@@ -1239,13 +1239,16 @@ def calculate_peak_power(data: BaseRaw, time_window: int = 3, threshold: float =
psp = scores.mean(axis=1) psp = scores.mean(axis=1)
bad_channels = list(compress(cast(list[str], data.ch_names), psp < threshold)) bad_channels = list(compress(cast(list[str], data.ch_names), psp < threshold))
plot_data = data.copy()
plot_data.info["bads"] = bad_channels
existing_bads = set(data.info.get("bads", [])) existing_bads = set(data.info.get("bads", []))
data.info["bads"] = list(existing_bads | set(bad_channels)) data.info["bads"] = list(existing_bads | set(bad_channels))
# Determine the colors based on the threshold, and create the figures # Determine the colors based on the threshold, and create the figures
color_stops = ([0.0, threshold, threshold+0.1, threshold+0.2, 1.0], [0.0, threshold, threshold, 1.0]) color_stops = ([0.0, threshold, threshold+0.1, threshold+0.2, 1.0], [0.0, threshold, threshold, 1.0])
psp1, psp2 = plot_timechannel_quality_metrics(data, scores, times, color_stops, threshold, "Peak Spectral Power") psp1, psp2 = plot_timechannel_quality_metrics(plot_data, scores, times, color_stops, threshold, "Peak Spectral Power")
print(f"thresh: {threshold}")
return list(compress(cast(list[str], getattr(data, "ch_names")), psp < threshold)), psp1, psp2 return list(compress(cast(list[str], getattr(data, "ch_names")), psp < threshold)), psp1, psp2
+111 -13
View File
@@ -19,6 +19,7 @@ from queue import Empty
from copy import deepcopy from copy import deepcopy
from pathlib import Path from pathlib import Path
from datetime import datetime from datetime import datetime
from functools import partial
from multiprocessing import Process, current_process, freeze_support, Queue, set_start_method from multiprocessing import Process, current_process, freeze_support, Queue, set_start_method
# External library imports # External library imports
@@ -28,8 +29,8 @@ from PySide6.QtWidgets import (
QApplication, QWidget, QMessageBox, QVBoxLayout, QHBoxLayout, QTextEdit, QScrollArea, QComboBox, QGridLayout, QSplitter, QDialogButtonBox, QHeaderView, QApplication, QWidget, QMessageBox, QVBoxLayout, QHBoxLayout, QTextEdit, QScrollArea, QComboBox, QGridLayout, QSplitter, QDialogButtonBox, QHeaderView,
QPushButton, QMainWindow, QLabel, QLineEdit, QGroupBox, QDialog, QMenu, QSpinBox, QTableWidget, QTableWidgetItem QPushButton, QMainWindow, QLabel, QLineEdit, QGroupBox, QDialog, QMenu, QSpinBox, QTableWidget, QTableWidgetItem
) )
from PySide6.QtCore import QEvent, Signal, Qt, QTimer from PySide6.QtCore import QEvent, QObject, Signal, Qt, QTimer
from PySide6.QtGui import QAction, QFontMetrics, QKeySequence, QIcon from PySide6.QtGui import QAction, QActionGroup, QFontMetrics, QKeySequence, QIcon
from PySide6.QtSvgWidgets import QSvgWidget # needed to show svgs when app is not frozen from PySide6.QtSvgWidgets import QSvgWidget # needed to show svgs when app is not frozen
from file_ext_registration import register_file_association, ELEVATION_FLAG from file_ext_registration import register_file_association, ELEVATION_FLAG
@@ -64,6 +65,7 @@ show_welcome_dialog = false
first_startup = true first_startup = true
[Preferences] [Preferences]
theme = auto
2d_data_bypass = false 2d_data_bypass = false
incompatible_save_bypass = false incompatible_save_bypass = false
missing_events_bypass = false missing_events_bypass = false
@@ -321,7 +323,7 @@ SECTIONS = [
BIDS_FIELD_MAP = { BIDS_FIELD_MAP = {
"BIDS - Age": "AGE", "BIDS - Age": "AGE",
"BIDS - Sex": "SEX", "BIDS - Sex": "SEX",
"BIDS - Hand": "HAND", "BIDS - Handedness": "HAND",
} }
@@ -509,6 +511,27 @@ class CustomApplication(QApplication):
return super().event(e) return super().event(e)
class ThemeChangeWatcher(QObject):
def __init__(self, main_window):
super().__init__()
self.main_window = main_window
self._theme_timer = QTimer(self)
self._theme_timer.setSingleShot(True)
self._theme_timer.setInterval(100)
self._theme_timer.timeout.connect(self._apply_theme)
def eventFilter(self, obj, event):
if event.type() == QEvent.Type.ApplicationPaletteChange:
# Restart the timer instead of updating immediately.
# Multiple palette-change events collapse into one update.
self._theme_timer.start()
return False
def _apply_theme(self):
print("OS theme changed")
self.main_window.update_theme()
class MainApplication(QMainWindow): class MainApplication(QMainWindow):
""" """
@@ -643,10 +666,10 @@ class MainApplication(QMainWindow):
self.left_v_splitter.setChildrenCollapsible(False) self.left_v_splitter.setChildrenCollapsible(False)
self.left_v_splitter.setMinimumWidth(460) self.left_v_splitter.setMinimumWidth(460)
top_left_container = QGroupBox("File Information") self.top_left_container = QGroupBox("File Information")
top_left_container.setStyleSheet("QGroupBox { font-weight: bold; }") self.top_left_container.setStyleSheet("QGroupBox { font-weight: bold; }")
top_left_container.setMinimumHeight(240) self.top_left_container.setMinimumHeight(240)
top_left_layout = QHBoxLayout(top_left_container) top_left_layout = QHBoxLayout(self.top_left_container)
self.top_left_widget = QTextEdit() self.top_left_widget = QTextEdit()
self.top_left_widget.setReadOnly(True) self.top_left_widget.setReadOnly(True)
@@ -659,22 +682,25 @@ class MainApplication(QMainWindow):
font_metrics = QFontMetrics(self.font()) font_metrics = QFontMetrics(self.font())
label_width = max(font_metrics.horizontalAdvance(key.capitalize()) for key in self.meta_fields) + 10 label_width = max(font_metrics.horizontalAdvance(key.capitalize()) for key in self.meta_fields) + 10
self.meta_labels = {}
for key, field in self.meta_fields.items(): for key, field in self.meta_fields.items():
row_layout = QHBoxLayout() row_layout = QHBoxLayout()
row_layout.setContentsMargins(0, 0, 0, 0) row_layout.setContentsMargins(0, 0, 0, 0)
row_layout.setSpacing(0) row_layout.setSpacing(0)
label = QLabel(key.capitalize() + ":") label = QLabel(key.capitalize() + ":")
self.meta_labels[key] = label
label.setFixedWidth(label_width) label.setFixedWidth(label_width)
row_layout.addWidget(label) row_layout.addWidget(label)
row_layout.addWidget(field) row_layout.addWidget(field)
right_column_layout.addLayout(row_layout) right_column_layout.addLayout(row_layout)
field.textChanged.connect(self.sync_bubble_data) field.textChanged.connect(self.sync_bubble_data)
label_desc = QLabel('<a href="#">Why are these useful?</a>') self.label_desc = QLabel('<a href="#">Why are these useful?</a>')
label_desc.setTextInteractionFlags(Qt.TextInteractionFlag.TextBrowserInteraction) self.label_desc.setTextInteractionFlags(Qt.TextInteractionFlag.TextBrowserInteraction)
label_desc.linkActivated.connect(lambda: QMessageBox.information(None, f"Info - {APP_NAME.upper()} ", "Age: Used in determing the participants PPF. Also used to assist in creating groups.\nGender: Used to assist in creating groups.\nHand: Used to assist in creating groups.\nGroup: Used to split participants into groups for comparisons between them.")) self.label_desc.linkActivated.connect(lambda: QMessageBox.information(None, f"Info - {APP_NAME.upper()} ", "Age: Used in determing the participants PPF. Also used to assist in creating groups.\nGender: Used to assist in creating groups.\nHand: Used to assist in creating groups.\nGroup: Used to split participants into groups for comparisons between them."))
right_column_layout.addWidget(label_desc) right_column_layout.addWidget(self.label_desc)
right_column_layout.addStretch() right_column_layout.addStretch()
self.right_column_widget.hide() self.right_column_widget.hide()
top_left_layout.addWidget(self.right_column_widget, stretch=1) top_left_layout.addWidget(self.right_column_widget, stretch=1)
@@ -688,7 +714,7 @@ class MainApplication(QMainWindow):
self.scroll_area.setWidget(self.bubble_container) self.scroll_area.setWidget(self.bubble_container)
self.scroll_area.setMinimumHeight(200) self.scroll_area.setMinimumHeight(200)
self.left_v_splitter.addWidget(top_left_container) self.left_v_splitter.addWidget(self.top_left_container)
self.left_v_splitter.addWidget(self.scroll_area) self.left_v_splitter.addWidget(self.scroll_area)
self.right_container = QWidget() self.right_container = QWidget()
@@ -845,6 +871,31 @@ class MainApplication(QMainWindow):
self.pref_actions = {} self.pref_actions = {}
preferences_menu = menu_bar.addMenu("Preferences") preferences_menu = menu_bar.addMenu("Preferences")
theme_menu = preferences_menu.addMenu("Theme")
theme_group = QActionGroup(self)
theme_group.setExclusive(True)
# 4. Define actions for the submenu
theme_actions = [
("Auto", "", "auto", resource_path("icons/warning_off_24dp_1F1F1F.svg"), "theme_auto"),
("Light", "", "light", resource_path("icons/warning_off_24dp_1F1F1F.svg"), "theme_light"),
("Dark", "", "dark", resource_path("icons/warning_off_24dp_1F1F1F.svg"), "theme_dark"),
]
for name, shortcut, mode, icon, config_key in theme_actions:
# Use partial to pass 'mode' to self.theme_change_func on click
slot = partial(self.theme_change_func, mode)
action = make_action(name, shortcut, slot, icon=icon, checkable=True)
theme_menu.addAction(action)
theme_group.addAction(action)
self.pref_actions[config_key] = action
# Set default selection (e.g., Auto)
self.pref_actions["theme_auto"].setChecked(True)
preferences_actions = [ preferences_actions = [
("2D Data Bypass", "", self.is_2d_bypass_func, resource_path("icons/warning_off_24dp_1F1F1F.svg"), "2d_data_bypass"), ("2D Data Bypass", "", self.is_2d_bypass_func, resource_path("icons/warning_off_24dp_1F1F1F.svg"), "2d_data_bypass"),
("Incompatible Save Bypass", "", self.incompatable_save_bypass_func, resource_path("icons/warning_off_24dp_1F1F1F.svg"), "incompatible_save_bypass"), ("Incompatible Save Bypass", "", self.incompatable_save_bypass_func, resource_path("icons/warning_off_24dp_1F1F1F.svg"), "incompatible_save_bypass"),
@@ -871,6 +922,31 @@ class MainApplication(QMainWindow):
self.statusbar.showMessage("Ready") self.statusbar.showMessage("Ready")
def update_theme(self):
text = self.label_desc.text()
self.label_desc.setText("")
self.label_desc.setText(text)
widgets = [
self.label_desc,
self.top_left_widget,
self.right_column_widget,
self.top_left_container,
]
widgets.extend(self.meta_fields.values())
widgets.extend(self.meta_labels.values())
for widget in widgets:
widget.style().unpolish(widget)
widget.style().polish(widget)
widget.update()
for section in self.param_sections:
print("hi")
section.update_theme_colors()
def update_sections(self, index): def update_sections(self, index):
self.current_section_index = index self.current_section_index = index
@@ -1157,6 +1233,19 @@ class MainApplication(QMainWindow):
self.top_left_widget.paste() # Trigger paste self.top_left_widget.paste() # Trigger paste
self.statusbar.showMessage("Pasted from clipboard") # Show status message self.statusbar.showMessage("Pasted from clipboard") # Show status message
def theme_change_func(self, mode):
app = QApplication.instance()
style_hints = app.styleHints()
if mode == "auto":
style_hints.setColorScheme(Qt.ColorScheme.Unknown)
elif mode == "light":
style_hints.setColorScheme(Qt.ColorScheme.Light)
elif mode == "dark":
style_hints.setColorScheme(Qt.ColorScheme.Dark)
def _update_config_setting(self, group, key, value): def _update_config_setting(self, group, key, value):
"""Helper to update memory configuration and save to disk.""" """Helper to update memory configuration and save to disk."""
# configparser expects string values # configparser expects string values
@@ -1855,7 +1944,7 @@ class MainApplication(QMainWindow):
if self.button3.isVisible(): if self.button3.isVisible():
msg = QMessageBox(self) msg = QMessageBox(self)
msg.setWindowTitle("Confirm - FLARES") msg.setWindowTitle("Confirm - FLARES")
msg.setText("Processing new data will clear the current analysis. Continue? (If you do not want this dialog box to appear, toggle 'Analysis Clearing Bypass' from the Preferences menu.)") msg.setText("Processing new data will clear the current analysis and close all other windows. Continue? (If you do not want this dialog box to appear, toggle 'Analysis Clearing Bypass' from the Preferences menu.)")
# Add the OK and Cancel buttons # Add the OK and Cancel buttons
msg.setStandardButtons(QMessageBox.StandardButton.Ok | QMessageBox.StandardButton.Cancel) msg.setStandardButtons(QMessageBox.StandardButton.Ok | QMessageBox.StandardButton.Cancel)
@@ -1876,6 +1965,13 @@ class MainApplication(QMainWindow):
for item in DATA_SCHEMA: for item in DATA_SCHEMA:
setattr(self, item["key"], {}) setattr(self, item["key"], {})
for bubble in self.bubble_widgets.values():
bubble.reset()
for widget in QApplication.topLevelWidgets():
if widget is not self and widget.isVisible():
widget.close()
self.button1.clicked.disconnect(self.on_run_task) self.button1.clicked.disconnect(self.on_run_task)
self.button1.setText("Cancel") self.button1.setText("Cancel")
self.button1.clicked.connect(self.cancel_task) self.button1.clicked.connect(self.cancel_task)
@@ -2673,6 +2769,8 @@ if __name__ == "__main__":
icon_ext = "icns" if PLATFORM_NAME == "darwin" else "ico" icon_ext = "icns" if PLATFORM_NAME == "darwin" else "ico"
app.setWindowIcon(QIcon(resource_path(f"icons/main.{icon_ext}"))) app.setWindowIcon(QIcon(resource_path(f"icons/main.{icon_ext}")))
window = MainApplication(file_to_open=startup_args.initial_file) window = MainApplication(file_to_open=startup_args.initial_file)
app.theme_watcher = ThemeChangeWatcher(window)
app.installEventFilter(app.theme_watcher)
app.file_open_requested.connect(window.project_manager.load_project) app.file_open_requested.connect(window.project_manager.load_project)
window.setWindowIcon(QIcon(resource_path(f"icons/main.{icon_ext}"))) window.setWindowIcon(QIcon(resource_path(f"icons/main.{icon_ext}")))
window.show() window.show()
+1 -2
View File
@@ -831,11 +831,10 @@ class ParticipantFoldChannelsWidget(FlaresBaseWidget):
self.scroll_area.setWidgetResizable(True) self.scroll_area.setWidgetResizable(True)
self.scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) self.scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
self.scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded) 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 # 2. Create the central canvas widget that inside the scroll block
self.scroll_content_widget = QWidget() self.scroll_content_widget = QWidget()
self.scroll_content_widget.setStyleSheet("background-color: #f1f3f5;")
# 3. Establish the strict 3-column layout grid engine # 3. Establish the strict 3-column layout grid engine
self.grid_layout = QGridLayout(self.scroll_content_widget) 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 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.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 PySide6.QtCore import QEvent, QPoint, QSize, QTimer, Qt, Signal
from src.shared.shareddata import APP_NAME, PIPELINE_STAGES from src.shared.shareddata import APP_NAME, PIPELINE_STAGES
@@ -323,10 +323,10 @@ class ParamSection(QWidget):
self._updating_checkstates = False self._updating_checkstates = False
# Title label # Title label
title_label = QLabel(section_data["title"]) self.title_label = QLabel(section_data["title"])
title_label.setStyleSheet("font-weight: bold; font-size: 14px; margin-top: 10px; margin-bottom: 5px;") self.title_label.setStyleSheet("font-weight: bold; font-size: 14px; margin-top: 10px; margin-bottom: 5px;")
layout.addWidget(title_label) layout.addWidget(self.title_label)
self.header_widgets.append(title_label) self.header_widgets.append(self.title_label)
# Horizontal line # Horizontal line
line = QFrame() line = QFrame()
@@ -444,6 +444,10 @@ class ParamSection(QWidget):
self.update_dependencies() 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): def is_different(self, val_a, val_b, param_type=None):
"""Compares two parameter values to determine if they differ.""" """Compares two parameter values to determine if they differ."""
type_str = str(param_type).lower() type_str = str(param_type).lower()
@@ -890,6 +894,7 @@ class ProgressBubble(QWidget):
border-radius: 10px; border-radius: 10px;
padding: 8px 12px; padding: 8px 12px;
background-color: #e0f0ff; background-color: #e0f0ff;
color: #000000;
} }
""") """)
@@ -985,6 +990,13 @@ class ProgressBubble(QWidget):
self.spinner_idx += 1 self.spinner_idx += 1
self._update_label_text() 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): class FlaresBaseWidget(QWidget):
def __init__(self, caller): def __init__(self, caller):
+72 -26
View File
@@ -34,7 +34,9 @@ class UpdateOptodesWindow(QWidget):
self.setWindowTitle(f"Update optode positions - {APP_NAME.upper()}") self.setWindowTitle(f"Update optode positions - {APP_NAME.upper()}")
self.resize(760, 200) 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 = QLineEdit()
self.line_edit_file_a.setReadOnly(True) self.line_edit_file_a.setReadOnly(True)
self.btn_browse_a = QPushButton("Browse .snirf") self.btn_browse_a = QPushButton("Browse .snirf")
@@ -171,9 +173,16 @@ class UpdateOptodesWindow(QWidget):
msg.exec() msg.exec()
def browse_file_a(self) -> None: def browse_file_a(self) -> None:
file_path, _ = QFileDialog.getOpenFileName(self, "Select SNIRF File", "", "SNIRF Files (*.snirf)") file_paths, _ = QFileDialog.getOpenFileNames(
if file_path: self,
self.line_edit_file_a.setText(file_path) "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: def browse_file_b(self) -> None:
file_path, _ = QFileDialog.getOpenFileName(self, "Select File", "", "Supported Files (*.txt *.xlsx)") 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) self.line_edit_file_b.setText(file_path)
def clear_files(self) -> None: def clear_files(self) -> None:
self.selected_snirf_files.clear()
self.line_edit_file_a.clear() self.line_edit_file_a.clear()
self.line_edit_file_b.clear() self.line_edit_file_b.clear()
@@ -189,40 +199,76 @@ class UpdateOptodesWindow(QWidget):
file_b = self.line_edit_file_b.text() file_b = self.line_edit_file_b.text()
suffix = self.line_edit_suffix.text().strip() or "flare" suffix = self.line_edit_suffix.text().strip() or "flare"
if not file_a: if not self.selected_snirf_files:
QMessageBox.critical(self, "Missing File", "Please select a SNIRF file.") QMessageBox.critical(self, "Missing File", "Please select at least one SNIRF file.")
return return
if not file_b: 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 return
# Get original filename without extension output_dir = QFileDialog.getExistingDirectory(
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(
self, self,
"Save SNIRF File As", "Select Output Directory"
suggested_name,
"SNIRF Files (*.snirf)"
) )
if not save_path: if not output_dir:
print("Save cancelled.") print("Save cancelled.")
return return
# Ensure .snirf extension output_path = Path(output_dir)
if not save_path.lower().endswith(".snirf"):
save_path += ".snirf" 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: try:
self.update_optode_positions(file_a=file_a, file_b=file_b, save_path=save_path) self.update_optode_positions(
except Exception as e: file_a=file_a,
QMessageBox.critical(self, "Error", f"Failed to write file:\n{e}") file_b=file_b,
return save_path=save_path
)
QMessageBox.information(self, "File Saved", f"File was saved to:\n{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( def update_optode_positions(
self, 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 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 # 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 raw.set_montage(initial_montage) # type: ignore
write_raw_snirf(raw, save_path) write_raw_snirf(raw, save_path)