mainly whitespace, but also some generic qol improvements
This commit is contained in:
1 parent
2a5e3f5606
commit
3a8ffbdf70
26 files changed
+1871
-1224
No files matched your search
+75
-16
@@ -38,6 +38,23 @@ if TYPE_CHECKING:
|
||||
from main import MainApplication
|
||||
|
||||
|
||||
class LoadProjectThread(QThread):
|
||||
finished_signal = Signal(object) # Emits the loaded project data dictionary
|
||||
error_signal = Signal(str) # Emits the error message string
|
||||
|
||||
def __init__(self, filename: str) -> None:
|
||||
super().__init__()
|
||||
self.filename = filename
|
||||
|
||||
def run(self) -> None:
|
||||
try:
|
||||
with open(self.filename, "rb") as f:
|
||||
project_data = pickle.load(f)
|
||||
self.finished_signal.emit(project_data)
|
||||
except Exception as e:
|
||||
self.error_signal.emit(str(e))
|
||||
|
||||
|
||||
class SaveProjectThread(QThread):
|
||||
finished_signal = Signal(str)
|
||||
error_signal = Signal(str)
|
||||
@@ -56,6 +73,25 @@ class SaveProjectThread(QThread):
|
||||
self.error_signal.emit(str(e))
|
||||
|
||||
|
||||
class LoadingOverlay(QDialog):
|
||||
def __init__(self, parent: Optional[QWidget] = None) -> None:
|
||||
super().__init__(parent)
|
||||
self.setWindowFlags(Qt.WindowType.Dialog | Qt.WindowType.FramelessWindowHint)
|
||||
self.setModal(True)
|
||||
self.setWindowModality(Qt.WindowModality.ApplicationModal)
|
||||
self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
|
||||
|
||||
layout = QVBoxLayout()
|
||||
layout.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
|
||||
label = QLabel("Loading Project…")
|
||||
label.setStyleSheet(
|
||||
"font-size: 18px; color: white; background-color: rgba(0,0,0,150);"
|
||||
" padding: 20px; border-radius: 10px;"
|
||||
)
|
||||
layout.addWidget(label)
|
||||
self.setLayout(layout)
|
||||
|
||||
|
||||
class SavingOverlay(QDialog):
|
||||
def __init__(self, parent: Optional[QWidget] = None) -> None:
|
||||
@@ -64,7 +100,7 @@ class SavingOverlay(QDialog):
|
||||
self.setModal(True)
|
||||
self.setWindowModality(Qt.WindowModality.ApplicationModal)
|
||||
self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
|
||||
|
||||
|
||||
layout = QVBoxLayout()
|
||||
layout.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
|
||||
@@ -72,7 +108,7 @@ class SavingOverlay(QDialog):
|
||||
label.setStyleSheet("font-size: 18px; color: white; background-color: rgba(0,0,0,150); padding: 20px; border-radius: 10px;")
|
||||
layout.addWidget(label)
|
||||
self.setLayout(layout)
|
||||
|
||||
|
||||
|
||||
class ProjectManager:
|
||||
"""
|
||||
@@ -118,7 +154,7 @@ class ProjectManager:
|
||||
return f.read(8) == b"\x89HDF\r\n\x1a\n"
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def open_file_dialog(self) -> None:
|
||||
"""Opens dialog to pick a single .snirf file."""
|
||||
file_path, _ = QFileDialog.getOpenFileName(
|
||||
@@ -171,6 +207,8 @@ class ProjectManager:
|
||||
)
|
||||
return
|
||||
|
||||
self._load_files_into_pipeline(snirf_files)
|
||||
|
||||
def load_dropped_files(self, file_paths: List[str]) -> None:
|
||||
"""Public entry point for handling files or folders dropped onto the UI."""
|
||||
if not file_paths:
|
||||
@@ -217,7 +255,7 @@ class ProjectManager:
|
||||
|
||||
# Delegate to internal pipeline
|
||||
self._load_files_into_pipeline(valid_files)
|
||||
|
||||
|
||||
def _load_files_into_pipeline(self, file_paths: List[str]) -> None:
|
||||
"""Loads .snirf files into UI using chunked batches and background workers."""
|
||||
app = self.app
|
||||
@@ -243,7 +281,7 @@ class ProjectManager:
|
||||
|
||||
# Filter out duplicates AND non-SNIRF files (including macOS ._ shadow files)
|
||||
new_files = [
|
||||
p for p in file_paths
|
||||
p for p in file_paths
|
||||
if p not in app.selected_paths and self.is_valid_snirf(p)
|
||||
]
|
||||
if not new_files:
|
||||
@@ -339,12 +377,31 @@ class ProjectManager:
|
||||
self.load_project(filename)
|
||||
|
||||
def load_project(self, filename: str) -> None:
|
||||
"""Loads a .flare project file into the application."""
|
||||
"""Triggers asynchronous loading of a .flare project file."""
|
||||
app = self.app
|
||||
|
||||
# 1. Show the custom loading overlay matching your saver design
|
||||
self.loading_overlay = LoadingOverlay(app)
|
||||
self.loading_overlay.resize(app.size())
|
||||
self.loading_overlay.move(app.pos())
|
||||
self.loading_overlay.show()
|
||||
|
||||
# 2. Fire up the background thread for pickle loading
|
||||
self.loader_thread = LoadProjectThread(filename)
|
||||
self.loader_thread.finished_signal.connect(
|
||||
lambda data: self._handle_loaded_data(filename, data)
|
||||
)
|
||||
self.loader_thread.error_signal.connect(self._handle_load_error)
|
||||
self.loader_thread.start()
|
||||
|
||||
def _handle_loaded_data(self, filename: str, data: dict) -> None:
|
||||
# Close overlay
|
||||
if hasattr(self, "loading_overlay"):
|
||||
self.loading_overlay.close()
|
||||
self.loading_overlay.deleteLater()
|
||||
|
||||
app = self.app
|
||||
try:
|
||||
with open(filename, "rb") as f:
|
||||
data = pickle.load(f)
|
||||
|
||||
checks = [
|
||||
("version", "<=1.1.7"),
|
||||
("file_metadata", "<=1.2.2"),
|
||||
@@ -358,7 +415,7 @@ class ProjectManager:
|
||||
f"This project was saved in an earlier version of {APP_NAME.upper()} ({ver_str}) "
|
||||
"and is potentially not compatible with this version. "
|
||||
)
|
||||
if getattr(app, "incompatible_save_bypass", False):
|
||||
if getattr(app, "processing_incompatible_save_bypass", False):
|
||||
QMessageBox.warning(
|
||||
app, f"Warning - {APP_NAME.upper()}", msg + "Attempting load."
|
||||
)
|
||||
@@ -438,16 +495,19 @@ class ProjectManager:
|
||||
if hasattr(app, "button3"):
|
||||
app.button3.setVisible(has_data)
|
||||
|
||||
|
||||
self.add_to_recent_projects(os.path.normpath(filename))
|
||||
|
||||
# Reset baselines cleanly
|
||||
self.reset_all_dirty_states()
|
||||
|
||||
QMessageBox.information(app, "Loaded", f"Project loaded from:\n{filename}")
|
||||
|
||||
except Exception as e:
|
||||
QMessageBox.critical(app, "Error", f"Failed to load project:\n{e}")
|
||||
QMessageBox.critical(app, "Error", f"Failed to process loaded project data:\n{e}")
|
||||
|
||||
def _handle_load_error(self, error_msg: str) -> None:
|
||||
if hasattr(self, "loading_overlay"):
|
||||
self.loading_overlay.close()
|
||||
self.loading_overlay.deleteLater()
|
||||
QMessageBox.critical(self.app, "Error", f"Failed to read project file:\n{error_msg}")
|
||||
|
||||
# =========================================================================
|
||||
# Project Saving (Save / Save As)
|
||||
@@ -863,7 +923,6 @@ def _get_bids_demographics(snirf_path: str) -> dict[str, str]:
|
||||
return {}
|
||||
|
||||
|
||||
|
||||
def extract_metadata_worker(file_name: str) -> dict[str, Any]:
|
||||
"""Runs in the separate worker process. Returns a clean dict."""
|
||||
|
||||
@@ -921,7 +980,7 @@ def extract_metadata_worker(file_name: str) -> dict[str, Any]:
|
||||
if "hand" in demographics:
|
||||
snirf_info["BIDS - Handedness"] = demographics["hand"]
|
||||
return snirf_info
|
||||
|
||||
|
||||
except Exception as e:
|
||||
print(f"Worker safely caught failure on {file_name}: {str(e)}")
|
||||
return {'status': 'error', 'reason': str(e)}
|
||||
|
||||
Reference in new issue
Block a user