-
Notifications
You must be signed in to change notification settings - Fork 48
Critical Bug Fix fixed image fusion on mac #411
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
3f73e42
919e41e
ad999e0
937d146
ac9b26b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,10 @@ | ||
| import time | ||
|
|
||
| from PySide6 import QtCore, QtWidgets | ||
| import logging | ||
| import os | ||
| import pydicom | ||
| import platform | ||
| import numpy as np | ||
| from pydicom import dcmread | ||
| from vtkmodules.util import numpy_support | ||
|
|
@@ -59,21 +62,34 @@ def load(self, interrupt_flag=None, progress_callback=None): | |
| Returns: | ||
| None. Emits the result via the signal_loaded or signal_error signals. | ||
| """ | ||
| # Store interrupt flag | ||
|
|
||
| # Wrap the progress_callback so it always runs in the main thread | ||
| def main_thread_progress_callback(*args, **kwargs): | ||
| if progress_callback is not None: | ||
| # If progress_callback happens to be a Qt Signal, use QMetaObject.invokeMethod to ensure main thread execution | ||
| if hasattr(progress_callback, "emit"): | ||
| QtCore.QMetaObject.invokeMethod( | ||
| progress_callback, | ||
| "emit", | ||
| QtCore.Qt.QueuedConnection, | ||
| QtCore.Q_ARG(object, args if len(args) > 1 else args[0]) | ||
| ) | ||
| else: | ||
| progress_callback(*args, **kwargs) | ||
|
|
||
| self._interrupt_flag = interrupt_flag | ||
| try: | ||
| # Check for interrupt before starting | ||
| if self._interrupt_flag is not None and self._interrupt_flag.is_set(): | ||
| if progress_callback is not None: | ||
| progress_callback.emit(("Loading cancelled", 0)) | ||
| self.signal_error.emit((False, "Loading cancelled")) | ||
| main_thread_progress_callback(("Loading cancelled", 0)) | ||
| if hasattr(progress_callback, "emit"): | ||
| self.signal_error.emit((False, "Loading cancelled")) | ||
| return | ||
| self._load_with_vtk(progress_callback) | ||
| self._load_with_vtk(main_thread_progress_callback) | ||
| except Exception as e: | ||
| if progress_callback is not None: | ||
| progress_callback.emit(("Error loading images", e)) | ||
| logging.exception("Error loading images: %s\n%s", e) | ||
| self.signal_error.emit((False, e)) | ||
| main_thread_progress_callback(("Error loading images", e)) | ||
| logging.exception("Error loading images: %s\n%s", e) | ||
| self.signal_error.emit((False, f"{e}")) | ||
|
|
||
| def _load_with_vtk(self, progress_callback): | ||
| """ | ||
|
|
@@ -97,7 +113,7 @@ def _load_with_vtk(self, progress_callback): | |
|
|
||
| # Progress: loading fixed image | ||
| if progress_callback is not None: | ||
| progress_callback.emit(("Loading fixed image (VTK)...", 10)) | ||
| progress_callback(("Loading fixed image (VTK)...", 10)) | ||
|
|
||
| # Check for interrupt before loading fixed | ||
| if self._interrupt_flag is not None and self._interrupt_flag.is_set(): | ||
|
|
@@ -123,7 +139,7 @@ def _load_with_vtk(self, progress_callback): | |
| "Manual fusion requires all files to be from the same directory." | ||
| ) | ||
| if progress_callback is not None: | ||
| progress_callback.emit(("Error loading images", error_msg)) | ||
| progress_callback(("Error loading images", error_msg)) | ||
| logging.error("manualFusionLoader.py_load_with_vtk: ", error_msg) | ||
| self.signal_error.emit((False, error_msg)) | ||
| return | ||
|
|
@@ -145,9 +161,11 @@ def _load_with_vtk(self, progress_callback): | |
| logging.warning("<manualFusionLoader.py_load_with_vtk>Error reading DICOM file", e) | ||
| continue | ||
|
|
||
|
|
||
| # Populate moving model container before processing with VTK so origin can be read the same way as ROI Transfer logic | ||
| moving_image_loader = MovingImageLoader(selected_image_files, None, self) | ||
| moving_model_populated = moving_image_loader.load_manual_mode(self._interrupt_flag, progress_callback) | ||
| moving_model_populated = moving_image_loader.load_manual_mode(self._interrupt_flag, | ||
| progress_callback) | ||
|
|
||
| if not moving_model_populated: | ||
| # Check if interrupted, emit cancel signal immediately | ||
|
|
@@ -167,7 +185,7 @@ def _load_with_vtk(self, progress_callback): | |
| raise RuntimeError("Failed to load fixed image with VTK.") | ||
|
|
||
| if progress_callback is not None: | ||
| progress_callback.emit(("Loading overlay image (VTK)...", 50)) | ||
| progress_callback(("Loading overlay image (VTK)...", 50)) | ||
|
|
||
| # Check for interrupt before loading moving | ||
| if self._interrupt_flag is not None and self._interrupt_flag.is_set(): | ||
|
|
@@ -184,7 +202,7 @@ def _load_with_vtk(self, progress_callback): | |
| if transform_file is not None: | ||
| try: | ||
| if progress_callback is not None: | ||
| progress_callback.emit(("Extracting saved transform...", 80)) | ||
| progress_callback(("Extracting saved transform...", 80)) | ||
| ds = pydicom.dcmread(transform_file) | ||
|
|
||
| # See explanation at top for more details on private tags | ||
|
|
@@ -195,14 +213,15 @@ def _load_with_vtk(self, progress_callback): | |
| except Exception as e: | ||
| logging.error(f"Error extracting transform from {transform_file}: {e}") | ||
| if progress_callback is not None: | ||
| progress_callback.emit(("Error extracting transform", 80)) | ||
| progress_callback(("Error extracting transform", 80)) | ||
| self.signal_error.emit((False, f"Error extracting transform: {e}")) | ||
| return | ||
|
|
||
| # Do any overlay generation or heavy work here if needed | ||
| # Last message before overlay loaded | ||
| if progress_callback is not None: | ||
| progress_callback.emit(("Preparing overlays...", 90)) | ||
| progress_callback(("Preparing overlays...", 90)) | ||
| QtCore.QCoreApplication.processEvents() | ||
| time.sleep(0.05) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. while it is only 0.05 seconds. Is this a thread blocking? could a non-thread blocking option be used here
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it doesn't need to be there at all it just goes too quick and would go 50% to done, was just thinking it would be better for the user to see it go to 90% for a very quick time before loading. can be removed if best |
||
|
|
||
| # Final interrupt check before emitting loaded signal | ||
| if self._interrupt_flag is not None and self._interrupt_flag.is_set(): | ||
|
|
@@ -217,7 +236,7 @@ def _load_with_vtk(self, progress_callback): | |
|
|
||
| # Emit 100% progress just before closing/loading is complete | ||
| if progress_callback is not None: | ||
| progress_callback.emit(("Complete", 100)) | ||
| progress_callback(("Complete", 100)) | ||
| QtCore.QCoreApplication.processEvents() | ||
|
|
||
| def _extracted_from__load_with_vtk_62(self, ds, np, transform_file): | ||
|
|
@@ -353,5 +372,4 @@ def on_manual_fusion_loaded(self, result): | |
| level = 40 | ||
| # Set these as the initial fusion window/level | ||
| patient_dict_container.set("fusion_window", window) | ||
| patient_dict_container.set("fusion_level", level) | ||
|
|
||
| patient_dict_container.set("fusion_level", level) | ||
Uh oh!
There was an error while loading. Please reload this page.