continuation of the rewrite

This commit is contained in:
2026-07-15 21:43:22 -07:00
parent 12afc5d3bc
commit d300dfb7f7
7 changed files with 132 additions and 329 deletions
+1 -1
View File
@@ -64,7 +64,7 @@ class CrossGroupBrainImageWidget(CrossGroupUIMixin, FlaresBaseWidget):
self.setup_cross_group_ui(["0 (Contrast Image)"]) self.setup_cross_group_ui(["0 (Contrast Image)"])
def proccess_request(self): def process_request(self):
request = self.get_common_request_data(PARAMETERIZED_INDEXES) request = self.get_common_request_data(PARAMETERIZED_INDEXES)
if request is None: if request is None:
+34 -153
View File
@@ -1,171 +1,58 @@
""" """
Filename: groupfunctionalconnectivity.py Filename: intergroupfunctionalconnectivity.py
Description: Group functional connectivity analysis window for FLARES Description: Logic for the Inter-Group Functional Connectivity analysis window
Author: Tyler de Zeeuw Author: Tyler de Zeeuw
License: GPL-3.0 License: GPL-3.0
""" """
import os # External library imports
from PySide6.QtWidgets import QMessageBox
from PySide6.QtWidgets import QComboBox, QDialog, QGridLayout, QHBoxLayout, QPushButton, QScrollArea, QWidget, QVBoxLayout, QLabel, QMessageBox from flares import run_group_functional_connectivity
from PySide6.QtCore import QSize from src.shared.flaresbasewidget import InterGroupUIMixin, FlaresBaseWidget
from src.shared.flaresbasewidget import FlaresBaseWidget, ParameterInputDialog
from src.shared.shareddata import APP_NAME from src.shared.shareddata import APP_NAME
class GroupFunctionalConnectivityWidget(FlaresBaseWidget): PARAMETERIZED_INDEXES = {
0: [
{
"key": "n_lines",
"label": "<Description>",
"default": "20",
"type": int,
},
{
"key": "vmin",
"label": "<Description>",
"default": "0.9",
"type": float,
},
],
}
class InterGroupFunctionalConnectivityWidget(InterGroupUIMixin, FlaresBaseWidget):
def __init__(self, haemo_dict, group, config_dict): def __init__(self, haemo_dict, group, config_dict):
super().__init__("GroupFunctionalConnectivityWidget") super().__init__("InterGroupFunctionalConnectivity")
self.setWindowTitle(f"Group Functional Connectivity Viewer [BETA] - {APP_NAME.upper()}") self.setWindowTitle(f"Inter-Group Functional Connectivity Viewer [BETA] - {APP_NAME.upper()}")
self.haemo_dict = haemo_dict self.haemo_dict = haemo_dict
self.group = group self.group = group
self.config_dict = config_dict self.config_dict = config_dict
self.show_all_events = True
self._updating_checkstates = False
QMessageBox.warning(self, f"Warning - {APP_NAME.upper()}", f"Functional Connectivity is still in development and the results should currently be taken with a grain of salt. " QMessageBox.warning(self, f"Warning - {APP_NAME.upper()}", f"Functional Connectivity is still in development and the results should currently be taken with a grain of salt. "
"By clicking OK, you accept that the images generated may not be factual.") "By clicking OK, you accept that the images generated may not be factual.")
self.setup_inter_group_ui(["0 (Betas)",])
# Create mappings: file_path -> participant label and dropdown display text
self.participant_map = {} # file_path -> "Participant 1"
self.participant_dropdown_items = [] # "Participant 1 (filename)"
for i, file_path in enumerate(self.haemo_dict.keys(), start=1):
short_label = f"Participant {i}"
display_label = f"{short_label} ({os.path.basename(file_path)})"
self.participant_map[file_path] = short_label
self.participant_dropdown_items.append(display_label)
self.layout = QVBoxLayout(self)
self.top_bar = QHBoxLayout()
self.layout.addLayout(self.top_bar)
self.group_to_paths = {}
for file_path, group_name in self.group.items():
self.group_to_paths.setdefault(group_name, []).append(file_path)
self.group_names = sorted(self.group_to_paths.keys())
self.group_dropdown = QComboBox()
self.group_dropdown.addItem("<None Selected>")
self.group_dropdown.addItems(self.group_names)
self.group_dropdown.setCurrentIndex(0)
self.group_dropdown.currentIndexChanged.connect(self.update_participant_list_for_group)
self.participant_dropdown = self._create_multiselect_dropdown(self.participant_dropdown_items)
self.participant_dropdown.currentIndexChanged.connect(self.update_participant_dropdown_label)
self.participant_dropdown.setEnabled(False)
self.event_dropdown = QComboBox()
self.event_dropdown.addItem("<None Selected>")
self.index_texts = [
"0 (Betas)",
#"1 (Significance)",
#"2 (Brain Activity Visualization)",
# "3 (fourth image)",
]
self.image_index_dropdown = self._create_multiselect_dropdown(self.index_texts)
self.image_index_dropdown.currentIndexChanged.connect(self.update_image_index_dropdown_label)
self.submit_button = QPushButton("Submit")
self.submit_button.clicked.connect(self.show_brain_images)
self.top_bar.addWidget(QLabel("Group:"))
self.top_bar.addWidget(self.group_dropdown)
self.top_bar.addWidget(QLabel("Participants:"))
self.top_bar.addWidget(self.participant_dropdown)
self.top_bar.addWidget(QLabel("Event:"))
self.top_bar.addWidget(self.event_dropdown)
self.top_bar.addWidget(QLabel("Image Indexes:"))
self.top_bar.addWidget(self.image_index_dropdown)
self.top_bar.addWidget(self.submit_button)
self.scroll = QScrollArea()
self.scroll.setWidgetResizable(True)
self.scroll_content = QWidget()
self.grid_layout = QGridLayout(self.scroll_content)
self.scroll.setWidget(self.scroll_content)
self.layout.addWidget(self.scroll)
self.thumb_size = QSize(280, 180)
self.showMaximized()
def show_brain_images(self): def process_request(self):
import flares as flares request = self.get_common_request_data(PARAMETERIZED_INDEXES)
if request is None:
selected_event = self.event_dropdown.currentText()
if selected_event == "<None Selected>":
selected_event = None
selected_display_names = self._get_checked_items(self.participant_dropdown)
selected_file_paths = []
for display_name in selected_display_names:
for fp, short_label in self.participant_map.items():
expected_display = f"{short_label} ({os.path.basename(fp)})"
if display_name == expected_display:
selected_file_paths.append(fp)
break
if selected_event:
valid_paths = []
for fp in selected_file_paths:
raw = self.haemo_dict.get(fp)
# Check if this participant actually has the event in their annotations
if raw is not None and hasattr(raw, "annotations"):
if selected_event in raw.annotations.description:
valid_paths.append(fp)
selected_file_paths = valid_paths
selected_indexes = [
int(s.split(" ")[0]) for s in self._get_checked_items(self.image_index_dropdown)
]
if not selected_file_paths:
print("No participants selected.")
return return
# Only keep indexes 0 and 1 that need parameters (selected_event, selected_file_paths, selected_indexes, param_values,) = request
parameterized_indexes = {
0: [
{
"key": "n_lines",
"label": "<Description>",
"default": "20",
"type": int,
},
{
"key": "vmin",
"label": "<Description>",
"default": "0.9",
"type": float,
},
],
}
# Inject full_text from index_texts
for idx, params_list in parameterized_indexes.items():
full_text = self.index_texts[idx] if idx < len(self.index_texts) else f"{idx} (No label found)"
for param_info in params_list:
param_info["full_text"] = full_text
indexes_needing_params = {idx: parameterized_indexes[idx] for idx in selected_indexes if idx in parameterized_indexes}
param_values = {}
if indexes_needing_params:
dialog = ParameterInputDialog(indexes_needing_params, parent=self)
if dialog.exec_() == QDialog.Accepted:
param_values = dialog.get_values()
if param_values is None:
return
else:
return
for idx in selected_indexes: for idx in selected_indexes:
if idx == 0: if idx == 0:
@@ -176,13 +63,7 @@ class GroupFunctionalConnectivityWidget(FlaresBaseWidget):
if n_lines is None or vmin is None: if n_lines is None or vmin is None:
print(f"Missing parameters for index {idx}, skipping.") print(f"Missing parameters for index {idx}, skipping.")
continue continue
flares.run_group_functional_connectivity(self.haemo_dict, self.config_dict, selected_file_paths, selected_event, 50, 0.5) run_group_functional_connectivity(self.haemo_dict, self.config_dict, selected_file_paths, selected_event, 50, 0.5)
elif idx == 1:
pass
elif idx == 2:
pass
elif idx == 3:
pass
else: else:
print(f"No method defined for index {idx}") print(f"No method defined for index {idx}")
+1 -1
View File
@@ -58,7 +58,7 @@ PARAMETERIZED_INDEXES = {
class ParticipantBrainViewerWidget(ParticipantUIMixin, FlaresBaseWidget): class ParticipantBrainViewerWidget(ParticipantUIMixin, FlaresBaseWidget):
def __init__(self, haemo_dict, cha_dict): def __init__(self, haemo_dict, cha_dict):
super().__init__("ParticipantBrainViewer") super().__init__("ParticipantBrain")
self.setWindowTitle(f"Participant Brain Viewer - {APP_NAME.upper()}") self.setWindowTitle(f"Participant Brain Viewer - {APP_NAME.upper()}")
self.haemo_dict = haemo_dict self.haemo_dict = haemo_dict
self.cha_dict = cha_dict self.cha_dict = cha_dict
+1 -1
View File
@@ -1,6 +1,6 @@
""" """
Filename: participantfoldchannels.py Filename: participantfoldchannels.py
Description: Participant fOLD channels analysis window for FLARES Description: Logic for the Participant fOLD Channels analysis window
Author: Tyler de Zeeuw Author: Tyler de Zeeuw
License: GPL-3.0 License: GPL-3.0
+82 -162
View File
@@ -1,183 +1,103 @@
""" """
Filename: participantfunctionalconnectivity.py Filename: participantfunctionalconnectivity.py
Description: Participant functional connectivity analysis window for FLARES Description: Logic for the Participant Functional Connectivity analysis window
Author: Tyler de Zeeuw Author: Tyler de Zeeuw
License: GPL-3.0 License: GPL-3.0
""" """
import os # External library imports
from PySide6.QtWidgets import QMessageBox
from PySide6.QtWidgets import QComboBox, QDialog, QGridLayout, QHBoxLayout, QPushButton, QScrollArea, QWidget, QVBoxLayout, QLabel, QMessageBox from flares import functional_connectivity_betas, functional_connectivity_envelope, functional_connectivity_spectral_epochs, functional_connectivity_spectral_time
from PySide6.QtCore import QSize from src.shared.flaresbasewidget import ParticipantUIMixin, FlaresBaseWidget
from src.shared.flaresbasewidget import FlaresBaseWidget, ParameterInputDialog
from src.shared.shareddata import APP_NAME from src.shared.shareddata import APP_NAME
class ParticipantFunctionalConnectivityWidget(FlaresBaseWidget): PARAMETERIZED_INDEXES = {
0: [
{
"key": "n_lines",
"label": "<Description>",
"default": "20",
"type": int,
},
{
"key": "vmin",
"label": "<Description>",
"default": "0.9",
"type": float,
},
],
1: [
{
"key": "n_lines",
"label": "<Description>",
"default": "20",
"type": int,
},
{
"key": "vmin",
"label": "<Description>",
"default": "0.9",
"type": float,
},
],
2: [
{
"key": "n_lines",
"label": "<Description>",
"default": "20",
"type": int,
},
{
"key": "vmin",
"label": "<Description>",
"default": "0.9",
"type": float,
},
],
3: [
{
"key": "n_lines",
"label": "<Description>",
"default": "20",
"type": int,
},
{
"key": "vmin",
"label": "<Description>",
"default": "0.9",
"type": float,
},
],
}
class ParticipantFunctionalConnectivityWidget(ParticipantUIMixin, FlaresBaseWidget):
def __init__(self, haemo_dict, epochs_dict): def __init__(self, haemo_dict, epochs_dict):
super().__init__("FunctionalConnectivityWidget") super().__init__("ParticipantFunctionalConnectivity")
self.setWindowTitle(f"Functional Connectivity Viewer [BETA] - {APP_NAME.upper()}") self.setWindowTitle(f"Participant Functional Connectivity Viewer [BETA] - {APP_NAME.upper()}")
self.haemo_dict = haemo_dict self.haemo_dict = haemo_dict
self.epochs_dict = epochs_dict self.epochs_dict = epochs_dict
QMessageBox.warning(self, f"Warning - {APP_NAME.upper()}", f"Functional Connectivity is still in development and the results should currently be taken with a grain of salt. " QMessageBox.warning(self, f"Warning - {APP_NAME.upper()}", f"Functional Connectivity is still in development and the results should currently be taken with a grain of salt. "
"By clicking OK, you accept that the images generated may not be factual.") "By clicking OK, you accept that the images generated may not be factual.")
# Create mappings: file_path -> participant label and dropdown display text self.setup_participant_ui(["0 (Spectral Connectivity Epochs)", "1 (Envelope Correlation)", "2 (Betas)", "3 (Spectral Connectivity Epochs)",])
self.participant_map = {} # file_path -> "Participant 1"
self.participant_dropdown_items = [] # "Participant 1 (filename)"
for i, file_path in enumerate(self.haemo_dict.keys(), start=1):
short_label = f"Participant {i}"
display_label = f"{short_label} ({os.path.basename(file_path)})"
self.participant_map[file_path] = short_label
self.participant_dropdown_items.append(display_label)
self.layout = QVBoxLayout(self)
self.top_bar = QHBoxLayout()
self.layout.addLayout(self.top_bar)
self.participant_dropdown = self._create_multiselect_dropdown(self.participant_dropdown_items)
self.participant_dropdown.currentIndexChanged.connect(self.update_participant_dropdown_label)
self.event_dropdown = QComboBox()
self.event_dropdown.addItem("<None Selected>")
self.index_texts = [
"0 (Spectral Connectivity Epochs)",
"1 (Envelope Correlation)",
"2 (Betas)",
"3 (Spectral Connectivity Epochs)",
]
self.image_index_dropdown = self._create_multiselect_dropdown(self.index_texts) def process_request(self):
self.image_index_dropdown.currentIndexChanged.connect(self.update_image_index_dropdown_label) request = self.get_common_request_data(PARAMETERIZED_INDEXES)
if request is None:
return
self.submit_button = QPushButton("Submit") (selected_event, selected_file_paths, selected_indexes, param_values,) = request
self.submit_button.clicked.connect(self.show_brain_images)
self.top_bar.addWidget(QLabel("Participants:"))
self.top_bar.addWidget(self.participant_dropdown)
self.top_bar.addWidget(QLabel("Event:"))
self.top_bar.addWidget(self.event_dropdown)
self.top_bar.addWidget(QLabel("Image Indexes:"))
self.top_bar.addWidget(self.image_index_dropdown)
self.top_bar.addWidget(self.submit_button)
self.scroll = QScrollArea()
self.scroll.setWidgetResizable(True)
self.scroll_content = QWidget()
self.grid_layout = QGridLayout(self.scroll_content)
self.scroll.setWidget(self.scroll_content)
self.layout.addWidget(self.scroll)
self.thumb_size = QSize(280, 180)
self.showMaximized()
def show_brain_images(self):
import flares as flares
selected_event = self.event_dropdown.currentText()
if selected_event == "<None Selected>":
selected_event = None
selected_display_names = self._get_checked_items(self.participant_dropdown)
selected_file_paths = []
for display_name in selected_display_names:
for fp, short_label in self.participant_map.items():
expected_display = f"{short_label} ({os.path.basename(fp)})"
if display_name == expected_display:
selected_file_paths.append(fp)
break
selected_indexes = [
int(s.split(" ")[0]) for s in self._get_checked_items(self.image_index_dropdown)
]
parameterized_indexes = {
0: [
{
"key": "n_lines",
"label": "<Description>",
"default": "20",
"type": int,
},
{
"key": "vmin",
"label": "<Description>",
"default": "0.9",
"type": float,
},
],
1: [
{
"key": "n_lines",
"label": "<Description>",
"default": "20",
"type": int,
},
{
"key": "vmin",
"label": "<Description>",
"default": "0.9",
"type": float,
},
],
2: [
{
"key": "n_lines",
"label": "<Description>",
"default": "20",
"type": int,
},
{
"key": "vmin",
"label": "<Description>",
"default": "0.9",
"type": float,
},
],
3: [
{
"key": "n_lines",
"label": "<Description>",
"default": "20",
"type": int,
},
{
"key": "vmin",
"label": "<Description>",
"default": "0.9",
"type": float,
},
],
}
# Inject full_text from index_texts
for idx, params_list in parameterized_indexes.items():
full_text = self.index_texts[idx] if idx < len(self.index_texts) else f"{idx} (No label found)"
for param_info in params_list:
param_info["full_text"] = full_text
indexes_needing_params = {idx: parameterized_indexes[idx] for idx in selected_indexes if idx in parameterized_indexes}
param_values = {}
if indexes_needing_params:
dialog = ParameterInputDialog(indexes_needing_params, parent=self)
if dialog.exec_() == QDialog.Accepted:
param_values = dialog.get_values()
if param_values is None:
return
else:
return
# Pass the necessary arguments to each method # Pass the necessary arguments to each method
for file_path in selected_file_paths: for file_path in selected_file_paths:
@@ -204,7 +124,7 @@ class ParticipantFunctionalConnectivityWidget(FlaresBaseWidget):
if n_lines is None or vmin is None: if n_lines is None or vmin is None:
print(f"Missing parameters for index {idx}, skipping.") print(f"Missing parameters for index {idx}, skipping.")
continue continue
flares.functional_connectivity_spectral_epochs(epochs_obj, n_lines, vmin) functional_connectivity_spectral_epochs(epochs_obj, n_lines, vmin)
elif idx == 1: elif idx == 1:
params = param_values.get(idx, {}) params = param_values.get(idx, {})
@@ -214,7 +134,7 @@ class ParticipantFunctionalConnectivityWidget(FlaresBaseWidget):
if n_lines is None or vmin is None: if n_lines is None or vmin is None:
print(f"Missing parameters for index {idx}, skipping.") print(f"Missing parameters for index {idx}, skipping.")
continue continue
flares.functional_connectivity_envelope(epochs_obj, n_lines, vmin) functional_connectivity_envelope(epochs_obj, n_lines, vmin)
elif idx == 2: elif idx == 2:
params = param_values.get(idx, {}) params = param_values.get(idx, {})
@@ -224,7 +144,7 @@ class ParticipantFunctionalConnectivityWidget(FlaresBaseWidget):
if n_lines is None or vmin is None: if n_lines is None or vmin is None:
print(f"Missing parameters for index {idx}, skipping.") print(f"Missing parameters for index {idx}, skipping.")
continue continue
flares.functional_connectivity_betas(haemo_obj, n_lines, vmin, selected_event) functional_connectivity_betas(haemo_obj, n_lines, vmin, selected_event)
elif idx == 3: elif idx == 3:
params = param_values.get(idx, {}) params = param_values.get(idx, {})
@@ -234,7 +154,7 @@ class ParticipantFunctionalConnectivityWidget(FlaresBaseWidget):
if n_lines is None or vmin is None: if n_lines is None or vmin is None:
print(f"Missing parameters for index {idx}, skipping.") print(f"Missing parameters for index {idx}, skipping.")
continue continue
flares.functional_connectivity_spectral_time(epochs_obj, n_lines, vmin) functional_connectivity_spectral_time(epochs_obj, n_lines, vmin)
else: else:
print(f"No method defined for index {idx}") print(f"No method defined for index {idx}")
+11 -9
View File
@@ -1,15 +1,17 @@
""" """
Filename: participant.py Filename: participantimage.py
Description: Participant analysis window for FLARES Description: Logic for the Participant Image analysis window
Author: Tyler de Zeeuw Author: Tyler de Zeeuw
License: GPL-3.0 License: GPL-3.0
""" """
# Built-in Imports
import os import os
from pathlib import Path from pathlib import Path
from datetime import datetime from datetime import datetime
# External library imports
from PySide6.QtWidgets import QGridLayout, QHBoxLayout, QMessageBox, QPushButton, QScrollArea, QWidget, QVBoxLayout, QLabel from PySide6.QtWidgets import QGridLayout, QHBoxLayout, QMessageBox, QPushButton, QScrollArea, QWidget, QVBoxLayout, QLabel
from PySide6.QtCore import Qt, QSize from PySide6.QtCore import Qt, QSize
from PySide6.QtGui import QPixmap from PySide6.QtGui import QPixmap
@@ -22,7 +24,7 @@ class ParticipantImageViewerWidget(FlaresBaseWidget):
def __init__(self, haemo_dict, fig_bytes_dict): def __init__(self, haemo_dict, fig_bytes_dict):
super().__init__("ParticipantImage") super().__init__("ParticipantImage")
self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose) self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)
self.setWindowTitle(f"Participant Viewer - {APP_NAME.upper()}") self.setWindowTitle(f"Participant Image Viewer - {APP_NAME.upper()}")
self.haemo_dict = haemo_dict self.haemo_dict = haemo_dict
self.fig_bytes_dict = fig_bytes_dict self.fig_bytes_dict = fig_bytes_dict
@@ -36,9 +38,9 @@ class ParticipantImageViewerWidget(FlaresBaseWidget):
self.participant_map[file_path] = short_label self.participant_map[file_path] = short_label
self.participant_dropdown_items.append(display_label) self.participant_dropdown_items.append(display_label)
self.layout = QVBoxLayout(self) self.main_layout = QVBoxLayout(self)
self.top_bar = QHBoxLayout() self.top_bar = QHBoxLayout()
self.layout.addLayout(self.top_bar) self.main_layout.addLayout(self.top_bar)
self.participant_dropdown = self._create_multiselect_dropdown(self.participant_dropdown_items) self.participant_dropdown = self._create_multiselect_dropdown(self.participant_dropdown_items)
self.participant_dropdown.currentIndexChanged.connect(self.update_participant_dropdown_label) self.participant_dropdown.currentIndexChanged.connect(self.update_participant_dropdown_label)
@@ -58,12 +60,12 @@ class ParticipantImageViewerWidget(FlaresBaseWidget):
self.top_bar.addWidget(self.image_index_dropdown) self.top_bar.addWidget(self.image_index_dropdown)
self.top_bar.addWidget(self.submit_button) self.top_bar.addWidget(self.submit_button)
self.scroll = QScrollArea() self.scroll_area = QScrollArea()
self.scroll.setWidgetResizable(True) self.scroll_area.setWidgetResizable(True)
self.scroll_content = QWidget() self.scroll_content = QWidget()
self.grid_layout = QGridLayout(self.scroll_content) self.grid_layout = QGridLayout(self.scroll_content)
self.scroll.setWidget(self.scroll_content) self.scroll_area.setWidget(self.scroll_content)
self.layout.addWidget(self.scroll) self.main_layout.addWidget(self.scroll_area)
self.thumb_size = QSize(280, 180) self.thumb_size = QSize(280, 180)
+1 -1
View File
@@ -1108,7 +1108,7 @@ class CrossGroupUIMixin:
self.submit_button = QPushButton("Submit") self.submit_button = QPushButton("Submit")
self.submit_button.clicked.connect(self.proccess_request) self.submit_button.clicked.connect(self.process_request)
self.top_bar.addWidget(QLabel("Group A:")) self.top_bar.addWidget(QLabel("Group A:"))