further issue fixes
This commit is contained in:
@@ -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
|
||||||
|
|||||||
91
main.py
91
main.py
@@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5061,6 +5101,39 @@ 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)
|
||||||
|
|||||||
Reference in New Issue
Block a user