unhardcoding and general cleanup
This commit is contained in:
+115
-19
@@ -7,6 +7,7 @@ License: GPL-3.0
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
|
||||
from PySide6.QtWidgets import QApplication, QComboBox, QDialog, QGridLayout, QHBoxLayout, QLabel, QLineEdit, QListView, QMessageBox, QPushButton, QScrollArea, QVBoxLayout, QWidget, QFrame, QSpinBox
|
||||
from PySide6.QtGui import QStandardItemModel, QStandardItem, QPixmap, QIntValidator, QDoubleValidator
|
||||
@@ -84,28 +85,53 @@ class ParameterInputDialog(QDialog):
|
||||
self.params_dict = params_dict
|
||||
self.inputs = {} # {(idx, param_key): QLineEdit}
|
||||
|
||||
layout = QVBoxLayout(self)
|
||||
main_layout = QVBoxLayout(self)
|
||||
intro_label = QLabel(
|
||||
"Some methods require parameters to continue:\n"
|
||||
"Clicking OK will simply use default values if input is left empty."
|
||||
)
|
||||
layout.addWidget(intro_label)
|
||||
main_layout.addWidget(intro_label)
|
||||
self.setMinimumWidth(400)
|
||||
self.scroll = QScrollArea()
|
||||
self.scroll.setWidgetResizable(True)
|
||||
self.scroll.setMaximumHeight(800)
|
||||
self.scroll.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
|
||||
self.scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
|
||||
|
||||
self.scroll_content = QWidget()
|
||||
self.scroll_layout = QVBoxLayout(self.scroll_content)
|
||||
self.scroll_layout.setContentsMargins(10, 10, 10, 10)
|
||||
self.scroll.setWidget(self.scroll_content)
|
||||
|
||||
main_layout.addWidget(self.scroll)
|
||||
|
||||
for idx, param_list in params_dict.items():
|
||||
full_text = param_list[0].get('full_text', f"Index [{idx}]")
|
||||
group_label = QLabel(f"{full_text} requires parameters:")
|
||||
group_label.setStyleSheet("font-weight: bold; margin-top: 10px;")
|
||||
layout.addWidget(group_label)
|
||||
self.scroll_layout.addWidget(group_label)
|
||||
|
||||
for param_info in param_list:
|
||||
label = QLabel(param_info["label"])
|
||||
layout.addWidget(label)
|
||||
self.scroll_layout.addWidget(label)
|
||||
|
||||
line_edit = QLineEdit(self)
|
||||
line_edit.setPlaceholderText(str(param_info.get("default", "")))
|
||||
layout.addWidget(line_edit)
|
||||
if param_info.get("type") == list:
|
||||
widget = QComboBox(self)
|
||||
# Convert options to string just in case they aren't
|
||||
options = [str(opt) for opt in param_info.get("options", [])]
|
||||
widget.addItems(options)
|
||||
|
||||
# Set default choice if it exists in the options list
|
||||
default_val = str(param_info.get("default", ""))
|
||||
if default_val in options:
|
||||
widget.setCurrentText(default_val)
|
||||
else:
|
||||
widget = QLineEdit(self)
|
||||
widget.setPlaceholderText(str(param_info.get("default", "")))
|
||||
|
||||
self.inputs[(idx, param_info["key"])] = line_edit
|
||||
self.scroll_layout.addWidget(widget)
|
||||
|
||||
self.inputs[(idx, param_info["key"])] = widget
|
||||
|
||||
# Buttons
|
||||
btn_layout = QHBoxLayout()
|
||||
@@ -113,7 +139,7 @@ class ParameterInputDialog(QDialog):
|
||||
cancel_btn = QPushButton("Cancel", self)
|
||||
btn_layout.addWidget(ok_btn)
|
||||
btn_layout.addWidget(cancel_btn)
|
||||
layout.addLayout(btn_layout)
|
||||
main_layout.addLayout(btn_layout)
|
||||
|
||||
ok_btn.clicked.connect(self.accept)
|
||||
cancel_btn.clicked.connect(self.reject)
|
||||
@@ -131,8 +157,11 @@ class ParameterInputDialog(QDialog):
|
||||
Returns None if validation fails (error dialog shown).
|
||||
"""
|
||||
values = {}
|
||||
for (idx, param_key), line_edit in self.inputs.items():
|
||||
text = line_edit.text().strip()
|
||||
for (idx, param_key), widget in self.inputs.items():
|
||||
if isinstance(widget, QComboBox):
|
||||
text = widget.currentText().strip()
|
||||
else:
|
||||
text = widget.text().strip()
|
||||
|
||||
# Find param info dict
|
||||
param_info = None
|
||||
@@ -164,14 +193,15 @@ class ParameterInputDialog(QDialog):
|
||||
val = False
|
||||
else:
|
||||
raise ValueError(f"Invalid bool value: {text}")
|
||||
elif param_type == str:
|
||||
elif param_type in (str, list):
|
||||
val = text
|
||||
else:
|
||||
val = text # fallback
|
||||
except (ValueError, TypeError):
|
||||
type_name = "list option" if param_type == list else param_type.__name__
|
||||
self._show_error(
|
||||
f"Invalid input for index {idx} parameter '{param_key}': '{text}'\n"
|
||||
f"Expected type: {param_type.__name__}"
|
||||
f"Expected type: {type_name}"
|
||||
)
|
||||
return None
|
||||
|
||||
@@ -1055,7 +1085,7 @@ class FlaresBaseWidget(QWidget):
|
||||
|
||||
class CrossGroupUIMixin:
|
||||
|
||||
def setup_cross_group_ui(self, index_texts):
|
||||
def setup_cross_group_ui(self, index_texts, placeholder_text=""):
|
||||
|
||||
self.group_to_paths = {}
|
||||
for file_path, group_name in self.group_dict.items():
|
||||
@@ -1130,6 +1160,10 @@ class CrossGroupUIMixin:
|
||||
self.scroll_content = QWidget()
|
||||
self.grid_layout = QGridLayout(self.scroll_content)
|
||||
self.scroll_area.setWidget(self.scroll_content)
|
||||
self.placeholder_label = QLabel(placeholder_text)
|
||||
self.grid_layout.addWidget(self.placeholder_label, 0, 0)
|
||||
self.placeholder_label.setWordWrap(True)
|
||||
self.placeholder_label.setScaledContents(True)
|
||||
self.main_layout.addWidget(self.scroll_area)
|
||||
|
||||
self.thumb_size = QSize(280, 180)
|
||||
@@ -1307,7 +1341,7 @@ class CSVUIMixin:
|
||||
|
||||
|
||||
class InterGroupUIMixin:
|
||||
def setup_inter_group_ui(self, index_texts):
|
||||
def setup_inter_group_ui(self, index_texts, placeholder_text=""):
|
||||
self.show_all_events = True
|
||||
self._updating_checkstates = False
|
||||
|
||||
@@ -1366,12 +1400,16 @@ class InterGroupUIMixin:
|
||||
self.scroll_content = QWidget()
|
||||
self.grid_layout = QGridLayout(self.scroll_content)
|
||||
self.scroll.setWidget(self.scroll_content)
|
||||
self.placeholder_label = QLabel(placeholder_text)
|
||||
self.grid_layout.addWidget(self.placeholder_label, 0, 0)
|
||||
self.placeholder_label.setWordWrap(True)
|
||||
self.placeholder_label.setScaledContents(True)
|
||||
self.layout.addWidget(self.scroll)
|
||||
|
||||
self.thumb_size = QSize(280, 180)
|
||||
self.showMaximized()
|
||||
|
||||
def get_common_request_data(self, parameterized_indexes):
|
||||
def get_common_request_data(self, parameterized_indexes, json_location=None, contrast_dfs=None):
|
||||
selected_event = self.event_dropdown.currentText()
|
||||
if selected_event == "<None Selected>":
|
||||
selected_event = None
|
||||
@@ -1403,9 +1441,7 @@ class InterGroupUIMixin:
|
||||
if not selected_file_paths:
|
||||
print("No participants selected.")
|
||||
return
|
||||
|
||||
# Only keep indexes 0 and 1 that need parameters
|
||||
|
||||
|
||||
|
||||
# Inject full_text from index_texts
|
||||
for idx, params_list in parameterized_indexes.items():
|
||||
@@ -1415,6 +1451,66 @@ class InterGroupUIMixin:
|
||||
|
||||
indexes_needing_params = {idx: parameterized_indexes[idx] for idx in selected_indexes if idx in parameterized_indexes}
|
||||
|
||||
dynamic_rois = []
|
||||
|
||||
# 1. Check for the JSON file and parse ROI names
|
||||
if os.path.exists(json_location):
|
||||
try:
|
||||
with open(json_location, 'r', encoding='utf-8') as f:
|
||||
regions_data = json.load(f)
|
||||
|
||||
# Extract "name" from each region under "regions_of_interest"
|
||||
regions_list = regions_data.get("regions_of_interest", [])
|
||||
dynamic_rois = [region["name"] for region in regions_list if "name" in region]
|
||||
|
||||
except Exception as e:
|
||||
# Safe log if JSON is corrupted or unreadable
|
||||
print(f"Error reading ROI configurations from {json_location}: {e}")
|
||||
|
||||
# Fallback to prevent UI crashes if JSON file doesn't exist or is empty
|
||||
if not dynamic_rois:
|
||||
dynamic_rois = ["Option 1", "Option 2"]
|
||||
|
||||
dynamic_contrasts = []
|
||||
if contrast_dfs:
|
||||
contrast_set = set()
|
||||
for fp in selected_file_paths:
|
||||
# Get the contrasts dictionary associated with this file path
|
||||
file_contrasts = contrast_dfs.get(fp, {})
|
||||
for contrast_name in file_contrasts.keys():
|
||||
# If no event is selected, display all contrasts.
|
||||
# If an event is selected, only keep contrasts containing the event name as a substring.
|
||||
if selected_event is None or selected_event in contrast_name:
|
||||
contrast_set.add(contrast_name)
|
||||
|
||||
# Sort them cleanly for the UI
|
||||
dynamic_contrasts = sorted(list(contrast_set))
|
||||
|
||||
# 2. Loop through the active parameters needing input and intercept 'roi_a' and 'roi_b'
|
||||
for idx, params_list in indexes_needing_params.items():
|
||||
for param_info in params_list:
|
||||
if param_info["key"] == "roi_a":
|
||||
# Inject options list dynamically
|
||||
param_info["options"] = dynamic_rois
|
||||
# Default to the very first item
|
||||
param_info["default"] = dynamic_rois[0] if dynamic_rois else ""
|
||||
|
||||
elif param_info["key"] == "roi_b":
|
||||
# Inject the same options list
|
||||
param_info["options"] = dynamic_rois
|
||||
# Default to the first item not taken (index 1), with safety fallbacks
|
||||
if len(dynamic_rois) > 1:
|
||||
param_info["default"] = dynamic_rois[1]
|
||||
elif len(dynamic_rois) == 1:
|
||||
param_info["default"] = dynamic_rois[0]
|
||||
else:
|
||||
param_info["default"] = ""
|
||||
|
||||
elif param_info["key"] == "contrast_name":
|
||||
param_info["options"] = dynamic_contrasts
|
||||
param_info["default"] = dynamic_contrasts[0] if dynamic_contrasts else ""
|
||||
|
||||
|
||||
param_values = {}
|
||||
if indexes_needing_params:
|
||||
dialog = ParameterInputDialog(indexes_needing_params, parent=self)
|
||||
|
||||
Reference in New Issue
Block a user