functional connectivity, pylance, and other improvements

This commit is contained in:
2026-08-22 23:38:13 -07:00
parent 19bd3f1279
commit e37275a1bb
14 changed files with 1455 additions and 841 deletions
+33 -18
View File
@@ -1,16 +1,21 @@
"""
Filename: updateoptodes.py
Description: Methods to update optode locations for FLARES
Note: Compliant with pylance strict type checking
Author: Tyler de Zeeuw
License: GPL-3.0
"""
# Built-in imports
import os
from pathlib import Path
from typing import Dict, Optional, Union
# External library imports
import pandas as pd
import numpy as np
import numpy.typing as npt
from PySide6.QtWidgets import QWidget, QVBoxLayout, QLabel, QHBoxLayout, QMessageBox, QLineEdit, QPushButton, QFileDialog
from PySide6.QtCore import Qt
@@ -24,7 +29,7 @@ from src.shared.shareddata import APP_NAME
class UpdateOptodesWindow(QWidget):
def __init__(self, parent=None):
def __init__(self, parent: Optional[QWidget] = None) -> None:
super().__init__(parent, Qt.WindowType.Window)
self.setWindowTitle(f"Update optode positions - {APP_NAME.upper()}")
self.resize(760, 200)
@@ -50,7 +55,6 @@ class UpdateOptodesWindow(QWidget):
self.btn_clear.clicked.connect(self.clear_files)
self.btn_go.clicked.connect(self.go_action)
# ---
layout = QVBoxLayout()
self.description = QLabel()
self.description.setTextFormat(Qt.TextFormat.RichText)
@@ -75,7 +79,7 @@ class UpdateOptodesWindow(QWidget):
help_btn_a = QPushButton("?")
help_btn_a.setFixedWidth(25)
help_btn_a.setToolTip(help_text_a)
help_btn_a.clicked.connect(lambda _, text=help_text_a: self.show_help_popup(text))
help_btn_a.clicked.connect(lambda: self.show_help_popup(help_text_a))
file_a_layout.addWidget(help_btn_a)
# Container for label + line_edit + browse button with tooltip
@@ -98,7 +102,7 @@ class UpdateOptodesWindow(QWidget):
help_btn_b = QPushButton("?")
help_btn_b.setFixedWidth(25)
help_btn_b.setToolTip(help_text_b)
help_btn_b.clicked.connect(lambda _, text=help_text_b: self.show_help_popup(text))
help_btn_b.clicked.connect(lambda: self.show_help_popup(help_text_b))
file_b_layout.addWidget(help_btn_b)
file_b_container = QWidget()
@@ -121,7 +125,7 @@ class UpdateOptodesWindow(QWidget):
help_btn_suffix = QPushButton("?")
help_btn_suffix.setFixedWidth(25)
help_btn_suffix.setToolTip(help_text_suffix)
help_btn_suffix.clicked.connect(lambda _, text=help_text_suffix: self.show_help_popup(text))
help_btn_suffix.clicked.connect(lambda: self.show_help_popup(help_text_suffix))
suffix_layout.addWidget(help_btn_suffix)
suffix_container = QWidget()
@@ -143,13 +147,13 @@ class UpdateOptodesWindow(QWidget):
self.setLayout(layout)
def show_help_popup(self, text):
def show_help_popup(self, text: str) -> None:
msg = QMessageBox(self)
msg.setWindowTitle(f"Parameter Info - {APP_NAME.upper()}")
msg.setText(text)
msg.exec()
def handle_link_click(self, link):
def handle_link_click(self, link: str) -> None:
if link == "custom_link":
msg = QMessageBox(self)
msg.setWindowTitle("Example Digitization File")
@@ -166,21 +170,21 @@ class UpdateOptodesWindow(QWidget):
msg.setText(text)
msg.exec()
def browse_file_a(self):
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)
def browse_file_b(self):
def browse_file_b(self) -> None:
file_path, _ = QFileDialog.getOpenFileName(self, "Select File", "", "Supported Files (*.txt *.xlsx)")
if file_path:
self.line_edit_file_b.setText(file_path)
def clear_files(self):
def clear_files(self) -> None:
self.line_edit_file_a.clear()
self.line_edit_file_b.clear()
def go_action(self):
def go_action(self) -> None:
file_a = self.line_edit_file_a.text()
file_b = self.line_edit_file_b.text()
suffix = self.line_edit_suffix.text().strip() or "flare"
@@ -220,7 +224,12 @@ class UpdateOptodesWindow(QWidget):
QMessageBox.information(self, "File Saved", f"File was saved to:\n{save_path}")
def update_optode_positions(self, file_a, file_b, save_path):
def update_optode_positions(
self,
file_a: Union[str, Path],
file_b: Union[str, Path],
save_path: Union[str, Path]
) -> None:
fiducials = {}
ch_positions = {}
@@ -247,16 +256,22 @@ class UpdateOptodesWindow(QWidget):
elif extension == '.xlsx':
# TODO: Bad! Why assume sheet1 has the contents?
df = pd.read_excel(file_b, sheet_name='Sheet1')
df = pd.read_excel(file_b, sheet_name='Sheet1') # type: ignore
def _get_block_data(df, block_id, row_mapping, scale=0.001):
def _get_block_data(
target_df: pd.DataFrame,
block_id: int,
row_mapping: Union[Dict[int, str], str],
scale: float = 0.001
) -> Dict[str, npt.NDArray[np.float64]]:
"""Isolates a block, cleans numeric data, and returns a scaled dictionary."""
# 1. Isolate and clean
block = df[df['block_id'] == block_id].iloc[:, [1, 2, 3]].copy()
block = target_df[target_df['block_id'] == block_id].iloc[:, [1, 2, 3]].copy()
block = block.apply(pd.to_numeric, errors='coerce')
# 2. Extract into dictionary based on mapping
result = {}
result: Dict[str, npt.NDArray[np.float64]] = {}
# If row_mapping is a dict (like {0: 'nz'}), use it directly
if isinstance(row_mapping, dict):
@@ -265,7 +280,7 @@ class UpdateOptodesWindow(QWidget):
result[key] = block.iloc[row_idx].to_numpy(dtype=float) * scale
# If row_mapping is a string prefix (like 'D' or 'S'), auto-generate keys
elif isinstance(row_mapping, str):
else:
for i in range(len(block)):
result[f"{row_mapping}{i+1}"] = block.iloc[i].to_numpy(dtype=float) * scale
@@ -292,5 +307,5 @@ class UpdateOptodesWindow(QWidget):
# Read the SNIRF file, set the montage, and write it back
raw = read_raw_snirf(file_a, preload=True)
raw.set_montage(initial_montage)
raw.set_montage(initial_montage) # type: ignore
write_raw_snirf(raw, save_path)