further issue fixes

This commit is contained in:
2026-02-02 13:08:00 -08:00
parent 22695a2281
commit 76df19f332
2 changed files with 91 additions and 10 deletions

View File

@@ -1,6 +1,14 @@
# Version 1.2.1 # Version 1.2.1
- Added a requirements.txt file to ensure compatibility - Added a requirements.txt file to ensure compatibility
- Added new options 'Missing Events Bypass' and 'Analysis Clearing Bypass' to the Preferences Menu
- Missing Events Bypass allows comparing events in the Group Viewers even if not all participants in the group have the event present. Fixes [Issue 28](https://git.research.dezeeuw.ca/tyler/flares/issues/28)
- Clicking Process after an analysis has been performed will now clear the existing analysis by default with a popup warning that the analysis will be cleared
- Analysis Clearing Bypass will prevent the popup and will not clear the existing analysis data. Fixes [Issue 41](https://git.research.dezeeuw.ca/tyler/flares/issues/41)
- Clicking 'Clear' should now actually properly clear all data. Hopefully fixes [Issue 9](https://git.research.dezeeuw.ca/tyler/flares/issues/9) for good
- Setting SHORT_CHANNEL to False will now grey out SHORT_CHANNEL_REGRESSION, as it is impossible to regress what does not exist. Sets SHORT_CHANNEL_REGRESSION to False under the hood when it is greyed out regardless of what is displayed. Fixes [Issue 47](https://git.research.dezeeuw.ca/tyler/flares/issues/47)
- Projects can now be saves if files have different parent folders. Fixes [Issue 48](https://git.research.dezeeuw.ca/tyler/flares/issues/48)
- It is no longer possible to attempt a save before any data has been processed. A popup will now display if a save is attempted with nothing to save
# Version 1.2.0 # Version 1.2.0

93
main.py
View File

@@ -46,7 +46,7 @@ from PySide6.QtGui import QAction, QKeySequence, QIcon, QIntValidator, QDoubleVa
from PySide6.QtSvgWidgets import QSvgWidget # needed to show svgs when app is not frozen from PySide6.QtSvgWidgets import QSvgWidget # needed to show svgs when app is not frozen
CURRENT_VERSION = "1.2.0" CURRENT_VERSION = "1.2.1"
API_URL = "https://git.research.dezeeuw.ca/api/v1/repos/tyler/flares/releases" API_URL = "https://git.research.dezeeuw.ca/api/v1/repos/tyler/flares/releases"
API_URL_SECONDARY = "https://git.research2.dezeeuw.ca/api/v1/repos/tyler/flares/releases" API_URL_SECONDARY = "https://git.research2.dezeeuw.ca/api/v1/repos/tyler/flares/releases"
@@ -1480,11 +1480,11 @@ class ParamSection(QWidget):
} }
""" """
def __init__(self, section_data): def __init__(self, section_data, global_widgets):
super().__init__() super().__init__()
layout = QVBoxLayout() layout = QVBoxLayout()
self.setLayout(layout) self.setLayout(layout)
self.widgets = {} self.widgets = global_widgets
self.dependencies = [] self.dependencies = []
self.selected_path = None self.selected_path = None
@@ -1527,7 +1527,7 @@ class ParamSection(QWidget):
widget = QComboBox() widget = QComboBox()
widget.addItems(["True", "False"]) widget.addItems(["True", "False"])
widget.setCurrentText(str(param["default"])) widget.setCurrentText(str(param["default"]))
widget.currentTextChanged.connect(self.update_dependencies) widget.currentTextChanged.connect(self.notify_global_update)
elif param["type"] == int: elif param["type"] == int:
widget = QLineEdit() widget = QLineEdit()
widget.setValidator(QIntValidator()) widget.setValidator(QIntValidator())
@@ -1541,7 +1541,7 @@ class ParamSection(QWidget):
widget = QComboBox() widget = QComboBox()
widget.addItems(param.get("options", [])) widget.addItems(param.get("options", []))
widget.setCurrentText(str(param.get("default", "<None Selected>"))) widget.setCurrentText(str(param.get("default", "<None Selected>")))
widget.currentTextChanged.connect(self.update_dependencies) widget.currentTextChanged.connect(self.notify_global_update)
else: else:
widget = self._create_multiselect_dropdown(None) widget = self._create_multiselect_dropdown(None)
elif param["type"] == range: elif param["type"] == range:
@@ -1580,6 +1580,16 @@ class ParamSection(QWidget):
self.update_dependencies() self.update_dependencies()
def notify_global_update(self):
"""
Since dependencies can cross sections, we need to tell
all sections to refresh their enabled/disabled states.
"""
# If you have a reference to the parent container, call its update.
# Otherwise, you can iterate through the known param_sections:
for section in self.parent().findChildren(ParamSection):
section.update_dependencies()
def update_dependencies(self): def update_dependencies(self):
"""Disables/Enables widgets based on parent selection values.""" """Disables/Enables widgets based on parent selection values."""
for dep in self.dependencies: for dep in self.dependencies:
@@ -1691,6 +1701,12 @@ class ParamSection(QWidget):
widget = info["widget"] widget = info["widget"]
expected_type = info["type"] expected_type = info["type"]
if name == "SHORT_CHANNEL_REGRESSION":
# If the widget is disabled (greyed out), force False
if not widget.isEnabled():
values[name] = False
continue
if expected_type == bool: if expected_type == bool:
values[name] = widget.currentText() == "True" values[name] = widget.currentText() == "True"
elif expected_type == list: elif expected_type == list:
@@ -4061,6 +4077,7 @@ class MainApplication(QMainWindow):
self.is_2d_bypass = False self.is_2d_bypass = False
self.incompatible_save_bypass = False self.incompatible_save_bypass = False
self.missing_events_bypass = False self.missing_events_bypass = False
self.analysis_clearing_bypass = False
self.files_total = 0 # total number of files to process self.files_total = 0 # total number of files to process
self.files_done = set() # set of file paths done (success or fail) self.files_done = set() # set of file paths done (success or fail)
@@ -4313,7 +4330,8 @@ class MainApplication(QMainWindow):
preferences_actions = [ preferences_actions = [
("2D Data Bypass", "", self.is_2d_bypass_func, resource_path("icons/info_24dp_1F1F1F.svg")), ("2D Data Bypass", "", self.is_2d_bypass_func, resource_path("icons/info_24dp_1F1F1F.svg")),
("Incompatible Save Bypass", "", self.incompatable_save_bypass_func, resource_path("icons/info_24dp_1F1F1F.svg")), ("Incompatible Save Bypass", "", self.incompatable_save_bypass_func, resource_path("icons/info_24dp_1F1F1F.svg")),
("Missing Events Bypass", "", self.missing_events_bypass_func, resource_path("icons/info_24dp_1F1F1F.svg")) ("Missing Events Bypass", "", self.missing_events_bypass_func, resource_path("icons/info_24dp_1F1F1F.svg")),
("Analysis Clearing Bypass", "", self.analysis_clearing_bypass_func, resource_path("icons/info_24dp_1F1F1F.svg"))
] ]
for name, shortcut, slot, icon in preferences_actions: for name, shortcut, slot, icon in preferences_actions:
preferences_menu.addAction(make_action(name, shortcut, slot, icon=icon, checkable=True, checked=False)) preferences_menu.addAction(make_action(name, shortcut, slot, icon=icon, checkable=True, checked=False))
@@ -4337,13 +4355,18 @@ class MainApplication(QMainWindow):
widget.deleteLater() widget.deleteLater()
self.param_sections.clear() self.param_sections.clear()
self.global_param_widgets = {}
# Add ParamSection widgets from SECTIONS # Add ParamSection widgets from SECTIONS
for section in SECTIONS: for section in SECTIONS:
self.section_widget = ParamSection(section) self.section_widget = ParamSection(section, self.global_param_widgets)
self.rows_layout.addWidget(self.section_widget) self.rows_layout.addWidget(self.section_widget)
self.param_sections.append(self.section_widget) self.param_sections.append(self.section_widget)
for sec in self.param_sections:
sec.update_dependencies()
def clear_all(self): def clear_all(self):
@@ -4358,6 +4381,11 @@ class MainApplication(QMainWindow):
if widget: if widget:
widget.deleteLater() widget.deleteLater()
if hasattr(self, "selected_paths"):
self.selected_paths = []
if hasattr(self, "selected_path"):
self.selected_path = None
# Clear file data # Clear file data
self.bubble_widgets.clear() self.bubble_widgets.clear()
self.statusBar().clearMessage() self.statusBar().clearMessage()
@@ -4405,6 +4433,9 @@ class MainApplication(QMainWindow):
def missing_events_bypass_func(self, checked): def missing_events_bypass_func(self, checked):
self.missing_events_bypass = checked self.missing_events_bypass = checked
def analysis_clearing_bypass_func(self, checked):
self.analysis_clearing_bypass = checked
def about_window(self): def about_window(self):
if self.about is None or not self.about.isVisible(): if self.about is None or not self.about.isVisible():
self.about = AboutWindow(self) self.about = AboutWindow(self)
@@ -4533,6 +4564,15 @@ class MainApplication(QMainWindow):
def save_project(self, onCrash=False): def save_project(self, onCrash=False):
if not getattr(self, 'raw_haemo_dict', None):
if not onCrash: # Don't show popups during a crash/autosave
QMessageBox.warning(
self,
"Save Project",
"There is no processed data to save. Please process some data before saving."
)
return
if not onCrash: if not onCrash:
filename, _ = QFileDialog.getSaveFileName( filename, _ = QFileDialog.getSaveFileName(
self, "Save Project", "", "FLARE Project (*.flare)" self, "Save Project", "", "FLARE Project (*.flare)"
@@ -4554,12 +4594,12 @@ class MainApplication(QMainWindow):
project_dir = project_path.parent project_dir = project_path.parent
file_list = [ file_list = [
str(PurePosixPath(Path(bubble.file_path).resolve().relative_to(project_dir))) str(PurePosixPath(os.path.relpath(Path(bubble.file_path).resolve(), project_dir)))
for bubble in self.bubble_widgets.values() for bubble in self.bubble_widgets.values()
] ]
progress_states = { progress_states = {
str(PurePosixPath(Path(bubble.file_path).resolve().relative_to(project_dir))): bubble.current_step str(PurePosixPath(os.path.relpath(Path(bubble.file_path).resolve(), project_dir))): bubble.current_step
for bubble in self.bubble_widgets.values() for bubble in self.bubble_widgets.values()
} }
@@ -5060,7 +5100,40 @@ class MainApplication(QMainWindow):
'''MODULE FILE''' '''MODULE FILE'''
def on_run_task(self): def on_run_task(self):
#do the check
if not self.analysis_clearing_bypass:
if self.button3.isVisible():
msg = QMessageBox(self)
msg.setWindowTitle("Confirm - FLARES")
msg.setText("Processing new data will clear the current analysis. Continue? (If you do not want this dialog box to appear, toggle 'Analysis Clearing Bypass' from the Preferences menu.)")
# Add the OK and Cancel buttons
msg.setStandardButtons(QMessageBox.StandardButton.Ok | QMessageBox.StandardButton.Cancel)
# Set the default button (highlighted)
msg.setDefaultButton(QMessageBox.StandardButton.Cancel)
# Capture the result
response = msg.exec()
if response == QMessageBox.StandardButton.Ok:
print("User clicked OK")
else:
return
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 = {}
self.button1.clicked.disconnect(self.on_run_task) self.button1.clicked.disconnect(self.on_run_task)
self.button1.setText("Cancel") self.button1.setText("Cancel")
self.button1.clicked.connect(self.cancel_task) self.button1.clicked.connect(self.cancel_task)