small rewrite to impose dry principles

This commit is contained in:
2026-07-15 21:21:35 -07:00
parent ffa14693b3
commit 12afc5d3bc
14 changed files with 1825 additions and 1716 deletions
+63 -72
View File
@@ -268,7 +268,13 @@ SECTIONS = [
{"name": "N_JOBS", "default": 1, "type": int, "help": "The number of CPUs to use to do the GLM computation. -1 means 'all CPUs'."},
]
},
{
{
"title": "Region of Interest",
"params": [
{"name": "JSON_LOCATION", "default": "", "type": str, "help": "Location of the JSON file containing region of interest results for significance calculations."},
]
},
{
"title": "Finishing Touches",
"params": [
# Intentionally empty (TODO)
@@ -287,6 +293,19 @@ SECTIONS = [
DATA_SCHEMA = [
{"key": "raw_haemo_dict", "help": "Dict[file_path, MNE RawArray]: Haemodynamic raw data"},
{"key": "epochs_dict", "help": "Dict[file_path, MNE Epochs]: Time-locked epoch data"},
{"key": "cha_dict", "help": "Dict[file_path, DataFrame]: Channel analysis results"},
{"key": "df_ind_dict", "help": "Dict[file_path, DataFrame]: Individual-level data/ROI results"},
{"key": "design_matrix_dict", "help": "Dict[file_path, DataFrame]: GLM design matrices"},
{"key": "config_dict", "help": "Dict[file_path, dict]: Processing configuration parameters"},
{"key": "fig_bytes_dict", "help": "Dict[file_path, dict]: Serialized figure data"},
{"key": "contrast_results_dict", "help": "Dict[file_path, dict]: Calculated contrast statistical results"},
{"key": "valid_dict", "help": "Dict[file_path, bool]: Boolean validity status per file"}
]
@@ -488,15 +507,9 @@ class MainApplication(QMainWindow):
# Initialization to ensure that saving can occur
self.raw_haemo_dict = {} # Processed Hemodynamic data
self.config_dict = {} # Analysis parameters/settings
self.epochs_dict = {} # Timing/Event data
self.cha_dict = {} # Channel configurations
self.contrast_results_dict = {} # Statistical results
self.df_ind_dict = {} # Individual dataframes
self.design_matrix_dict = {} # GLM Design matrices
self.valid_dict = {} # Quality control/Validity flags
self.fig_bytes_dict = {} # Cached plot images (serialized)
for item in DATA_SCHEMA:
setattr(self, item["key"], {})
self.file_metadata = {} # AGE, GENDER, GROUP
self.metadata_cache = {} # Internal file/path information metadata cache
self.bubble_widgets = {} # References to the UI "Bubble" objects
@@ -878,15 +891,8 @@ class MainApplication(QMainWindow):
self.files_done = set()
self.files_failed = set()
self.raw_haemo_dict = {}
self.config_dict = {}
self.epochs_dict = {}
self.fig_bytes_dict = {}
self.cha_dict = {}
self.contrast_results_dict = {}
self.df_ind_dict = {}
self.design_matrix_dict = {}
self.valid_dict = {}
for item in DATA_SCHEMA:
setattr(self, item["key"], {})
self.metadata_cache = {}
@@ -1045,7 +1051,22 @@ class MainApplication(QMainWindow):
def open_launcher_window(self):
self.launcher_window = ViewerLauncherWidget(self.raw_haemo_dict, self.config_dict, self.fig_bytes_dict, self.cha_dict, self.contrast_results_dict, self.df_ind_dict, self.design_matrix_dict, self.epochs_dict, self.folding_bypass)
data_map = {item["key"]: getattr(self, item["key"]) for item in DATA_SCHEMA}
# 2. Extract values in the specific order the widget constructor expects
args = [
data_map["raw_haemo_dict"],
data_map["epochs_dict"],
data_map["cha_dict"],
data_map["df_ind_dict"],
data_map["design_matrix_dict"],
data_map["config_dict"],
data_map["fig_bytes_dict"],
data_map["contrast_results_dict"],
self.folding_bypass
]
self.launcher_window = ViewerLauncherWidget(*args)
self.launcher_window.show()
def copy_text(self):
@@ -1188,7 +1209,7 @@ class MainApplication(QMainWindow):
def open_folder_dialog(self):
folder_path = QFileDialog.getExistingDirectory(self, "Select Folder", "")
if folder_path:
snirf_files = [os.path.normpath(str(f)) for f in Path(folder_path).glob("*.snirf")]
snirf_files = [os.path.normpath(str(f)) for f in Path(folder_path).rglob("*.snirf")]
self._load_files_into_pipeline(snirf_files)
@@ -1297,7 +1318,10 @@ class MainApplication(QMainWindow):
has_param_changes = any(section.has_any_changes() for section in self.param_sections)
# Check if there is processed data
has_processed_data = bool(getattr(self, 'raw_haemo_dict', None))
has_processed_data = any(
len(getattr(self, item["key"], {})) > 0
for item in DATA_SCHEMA
)
if not (has_processed_data or has_metadata or has_param_changes):
if not onCrash: # Don't show popups during a crash/autosave
@@ -1368,23 +1392,17 @@ class MainApplication(QMainWindow):
current_params = self.config_dict[first_file]
version = CURRENT_VERSION
project_data = {
project_data = {item["key"]: getattr(self, item["key"]) for item in DATA_SCHEMA}
project_data.update({
"version": version,
"file_list": file_list,
"progress_states": progress_states,
"raw_haemo_dict": self.raw_haemo_dict,
"file_metadata": rel_metadata,
"file_parameters": rel_file_params,
"config_dict": self.config_dict,
"epochs_dict": self.epochs_dict,
"fig_bytes_dict": self.fig_bytes_dict,
"cha_dict": self.cha_dict,
"current_ui_params": current_params,
"contrast_results_dict": self.contrast_results_dict,
"df_ind_dict": self.df_ind_dict,
"design_matrix_dict": self.design_matrix_dict,
"valid_dict": self.valid_dict,
}
})
def sanitize(obj):
if isinstance(obj, Path):
@@ -1472,15 +1490,9 @@ class MainApplication(QMainWindow):
return
self.raw_haemo_dict = data.get("raw_haemo_dict", {})
self.config_dict = data.get("config_dict", {})
self.epochs_dict = data.get("epochs_dict", {})
self.fig_bytes_dict = data.get("fig_bytes_dict", {})
self.cha_dict = data.get("cha_dict", {})
self.contrast_results_dict = data.get("contrast_results_dict", {})
self.df_ind_dict = data.get("df_ind_dict", {})
self.design_matrix_dict = data.get("design_matrix_dict", {})
self.valid_dict = data.get("valid_dict", {})
for item in DATA_SCHEMA:
key = item["key"]
setattr(self, key, data.get(key, {}))
project_dir = Path(filename).parent
@@ -1530,7 +1542,7 @@ class MainApplication(QMainWindow):
first_file = next(iter(self.config_dict.keys()))
self.restore_sections_from_config(self.config_dict[first_file])
has_data = bool(self.raw_haemo_dict)
has_data = any(len(getattr(self, item["key"], {})) > 0 for item in DATA_SCHEMA)
self.button1.setVisible(not has_data)
self.button3.setVisible(has_data)
@@ -1955,15 +1967,8 @@ class MainApplication(QMainWindow):
self.button3.setVisible(False)
self.raw_haemo_dict = {}
self.config_dict = {}
self.epochs_dict = {}
self.fig_bytes_dict = {}
self.cha_dict = {}
self.contrast_results_dict = {}
self.df_ind_dict = {}
self.design_matrix_dict = {}
self.valid_dict = {}
for item in DATA_SCHEMA:
setattr(self, item["key"], {})
self.button1.clicked.disconnect(self.on_run_task)
self.button1.setText("Cancel")
@@ -2091,27 +2096,13 @@ class MainApplication(QMainWindow):
# print(f"[DEBUG] Progress: {len(self.files_done)} / {self.files_total}")
if msg.get("success"):
# Unpack the massive tuple
raw_haemo, config, epochs, fig_bytes, cha, contrast, df_ind, design, valid = msg["result"]
# Initialize dictionaries once if needed
if not hasattr(self, 'raw_haemo_dict') or self.raw_haemo_dict is None:
attrs = ['raw_haemo_dict', 'config_dict', 'epochs_dict', 'fig_bytes_dict',
'cha_dict', 'contrast_results_dict', 'df_ind_dict',
'design_matrix_dict', 'valid_dict']
for attr in attrs:
setattr(self, attr, {})
self.files_results[file_path] = msg["result"]
self.raw_haemo_dict[file_path] = raw_haemo
self.config_dict[file_path] = config
self.epochs_dict[file_path] = epochs
self.fig_bytes_dict[file_path] = fig_bytes
self.cha_dict[file_path] = cha
self.contrast_results_dict[file_path] = contrast
self.df_ind_dict[file_path] = df_ind
self.design_matrix_dict[file_path] = design
self.valid_dict[file_path] = valid
results = msg["result"]
self.files_results[file_path] = results
# Simple, clean assignment
for item, value in zip(DATA_SCHEMA, results):
getattr(self, item["key"])[file_path] = value
self.statusbar.showMessage(f"Processed: {os.path.basename(file_path)}")