functional connectivity, pylance, and other improvements
This commit is contained in:
+33
-18
@@ -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)
|
||||
Reference in New Issue
Block a user