ui.dl_selection_page
1from PyQt6.QtGui import QIcon, QFont 2from PyQt6.QtWidgets import ( 3 QWidget, QVBoxLayout, QLabel, QPushButton, QGridLayout, QHBoxLayout, 4 QMessageBox, QGroupBox, QListWidget, QDialog, QLineEdit, QDialogButtonBox, QListWidgetItem 5) 6from PyQt6.QtCore import Qt, QCoreApplication 7import os 8 9from components.file_selector_widget import FileSelectorWidget 10from ui.dl_execution_page import DlExecutionPage 11from page import Page 12from logger import get_logger 13 14log = get_logger() 15 16 17class DlNiftiSelectionPage(Page): 18 """ 19 GUI page that allows the user to select NIfTI files for Deep Learning-based segmentation. 20 21 This page is part of a multi-step workflow: 22 - Displays a title and a FileSelectorWidget to pick one or more NIfTI files. 23 - Checks if a segmentation already exists for a given subject. 24 - Saves selections in a shared context and navigates to the next page (DlExecutionPage). 25 """ 26 27 def __init__(self, context=None, previous_page=None): 28 """ 29 Initialize the patient selection page. 30 31 :param context: Shared application context dictionary (used for data persistence between pages) 32 :param previous_page: Reference to the previous Page object (for navigation) 33 """ 34 super().__init__() 35 self.context = context 36 self.previous_page = previous_page 37 self.next_page = None # Will be created when advancing 38 39 self._setup_ui() 40 self._translate_ui() 41 42 # Re-translate the UI dynamically if the language changes in the context 43 if context and "language_changed" in context: 44 context["language_changed"].connect(self._translate_ui) 45 46 def _setup_ui(self): 47 """Create and organize all UI elements for this page.""" 48 self.layout = QVBoxLayout(self) 49 self.setLayout(self.layout) 50 51 # Title label 52 self.title = QLabel("Select NIfTI files for Deep Learning Segmentation") 53 self.title.setFont(QFont("Arial", 18, QFont.Weight.Bold)) 54 self.title.setAlignment(Qt.AlignmentFlag.AlignCenter) 55 self.layout.addWidget(self.title) 56 57 # Widget for selecting NIfTI files 58 # - `has_existing_function` allows pre-checking if a segmentation already exists 59 self.file_selector_widget = FileSelectorWidget( 60 parent=self, 61 context=self.context, 62 has_existing_function=self.has_existing_segmentation, 63 label="seg", 64 allow_multiple=True 65 ) 66 self.layout.addWidget(self.file_selector_widget) 67 68 # Pushes everything upward visually 69 self.layout.addStretch() 70 71 # Label for displaying temporary status or messages 72 self.status_label = QLabel("") 73 self.status_label.setAlignment(Qt.AlignmentFlag.AlignCenter) 74 self.layout.addWidget(self.status_label) 75 76 def has_existing_segmentation(self, nifti_file_path, workspace_path): 77 """ 78 Check if a deep learning segmentation already exists for the patient corresponding to this NIfTI file. 79 80 :param nifti_file_path: Full path to the selected NIfTI file. 81 :param workspace_path: Root path of the working directory. 82 :return: True if a segmentation file is already present, otherwise False. 83 """ 84 # Extract patient ID (subject ID) from the NIfTI file path 85 path_parts = nifti_file_path.replace(workspace_path, '').strip(os.sep).split(os.sep) 86 87 # Look for a directory segment starting with 'sub-' (BIDS convention) 88 subject_id = None 89 for part in path_parts: 90 if part.startswith('sub-'): 91 subject_id = part 92 break 93 94 # If no subject identifier found, no segmentation can be associated 95 if not subject_id: 96 return False 97 98 # Expected directory where segmentations are stored 99 seg_dir = os.path.join(workspace_path, 'derivatives', 'deep_learning_seg', subject_id, 'anat') 100 101 # If the segmentation directory does not exist, return False 102 if not os.path.exists(seg_dir): 103 return False 104 105 # Check for any segmentation file in the directory 106 for file in os.listdir(seg_dir): 107 if file.endswith('_seg.nii.gz') or file.endswith('_seg.nii'): 108 return True 109 110 return False 111 112 def back(self): 113 """ 114 Navigate back to the previous page in the workflow. 115 116 :return: The previous Page instance, or None if there is none. 117 """ 118 if self.previous_page: 119 self.previous_page.on_enter() 120 return self.previous_page 121 return None 122 123 def next(self, context): 124 """ 125 Save the selected files in the context and move to the next page (DlExecutionPage). 126 127 :param context: The shared application context dictionary. 128 :return: The next Page instance. 129 """ 130 # Store selected files in the context 131 if self.context is not None: 132 self.context["selected_segmentation_files"] = self.file_selector_widget.get_selected_files() 133 134 # Instantiate the next page only once 135 if not self.next_page: 136 self.next_page = DlExecutionPage(self.context, self) 137 if "history" in self.context: 138 self.context["history"].append(self.next_page) 139 140 # Prepare the next page before displaying 141 self.next_page.on_enter() 142 return self.next_page 143 144 def on_enter(self): 145 """Called when the page is shown — resets transient UI elements.""" 146 self.status_label.setText("") 147 148 def is_ready_to_advance(self): 149 """ 150 Check if the user can proceed to the next page. 151 152 :return: True if one or more files are selected. 153 """ 154 return self.file_selector_widget.get_selected_files() 155 156 def is_ready_to_go_back(self): 157 """ 158 Check if the user can navigate backward. 159 160 :return: Always True for this page. 161 """ 162 return True 163 164 def reset_page(self): 165 """ 166 Reset the page to its initial state, clearing all selections and messages. 167 """ 168 self.file_selector_widget._selected_files = [] 169 170 self.status_label.setText("") 171 172 # Clear any saved data in the context 173 if self.context: 174 self.context["selected_segmentation_files"] = [] 175 176 def _translate_ui(self): 177 """Apply translations to all text elements in the UI.""" 178 self.title.setText(QCoreApplication.translate( 179 "DlSelectionPage", 180 "Select NIfTI files for Deep Learning Segmentation" 181 ))
18class DlNiftiSelectionPage(Page): 19 """ 20 GUI page that allows the user to select NIfTI files for Deep Learning-based segmentation. 21 22 This page is part of a multi-step workflow: 23 - Displays a title and a FileSelectorWidget to pick one or more NIfTI files. 24 - Checks if a segmentation already exists for a given subject. 25 - Saves selections in a shared context and navigates to the next page (DlExecutionPage). 26 """ 27 28 def __init__(self, context=None, previous_page=None): 29 """ 30 Initialize the patient selection page. 31 32 :param context: Shared application context dictionary (used for data persistence between pages) 33 :param previous_page: Reference to the previous Page object (for navigation) 34 """ 35 super().__init__() 36 self.context = context 37 self.previous_page = previous_page 38 self.next_page = None # Will be created when advancing 39 40 self._setup_ui() 41 self._translate_ui() 42 43 # Re-translate the UI dynamically if the language changes in the context 44 if context and "language_changed" in context: 45 context["language_changed"].connect(self._translate_ui) 46 47 def _setup_ui(self): 48 """Create and organize all UI elements for this page.""" 49 self.layout = QVBoxLayout(self) 50 self.setLayout(self.layout) 51 52 # Title label 53 self.title = QLabel("Select NIfTI files for Deep Learning Segmentation") 54 self.title.setFont(QFont("Arial", 18, QFont.Weight.Bold)) 55 self.title.setAlignment(Qt.AlignmentFlag.AlignCenter) 56 self.layout.addWidget(self.title) 57 58 # Widget for selecting NIfTI files 59 # - `has_existing_function` allows pre-checking if a segmentation already exists 60 self.file_selector_widget = FileSelectorWidget( 61 parent=self, 62 context=self.context, 63 has_existing_function=self.has_existing_segmentation, 64 label="seg", 65 allow_multiple=True 66 ) 67 self.layout.addWidget(self.file_selector_widget) 68 69 # Pushes everything upward visually 70 self.layout.addStretch() 71 72 # Label for displaying temporary status or messages 73 self.status_label = QLabel("") 74 self.status_label.setAlignment(Qt.AlignmentFlag.AlignCenter) 75 self.layout.addWidget(self.status_label) 76 77 def has_existing_segmentation(self, nifti_file_path, workspace_path): 78 """ 79 Check if a deep learning segmentation already exists for the patient corresponding to this NIfTI file. 80 81 :param nifti_file_path: Full path to the selected NIfTI file. 82 :param workspace_path: Root path of the working directory. 83 :return: True if a segmentation file is already present, otherwise False. 84 """ 85 # Extract patient ID (subject ID) from the NIfTI file path 86 path_parts = nifti_file_path.replace(workspace_path, '').strip(os.sep).split(os.sep) 87 88 # Look for a directory segment starting with 'sub-' (BIDS convention) 89 subject_id = None 90 for part in path_parts: 91 if part.startswith('sub-'): 92 subject_id = part 93 break 94 95 # If no subject identifier found, no segmentation can be associated 96 if not subject_id: 97 return False 98 99 # Expected directory where segmentations are stored 100 seg_dir = os.path.join(workspace_path, 'derivatives', 'deep_learning_seg', subject_id, 'anat') 101 102 # If the segmentation directory does not exist, return False 103 if not os.path.exists(seg_dir): 104 return False 105 106 # Check for any segmentation file in the directory 107 for file in os.listdir(seg_dir): 108 if file.endswith('_seg.nii.gz') or file.endswith('_seg.nii'): 109 return True 110 111 return False 112 113 def back(self): 114 """ 115 Navigate back to the previous page in the workflow. 116 117 :return: The previous Page instance, or None if there is none. 118 """ 119 if self.previous_page: 120 self.previous_page.on_enter() 121 return self.previous_page 122 return None 123 124 def next(self, context): 125 """ 126 Save the selected files in the context and move to the next page (DlExecutionPage). 127 128 :param context: The shared application context dictionary. 129 :return: The next Page instance. 130 """ 131 # Store selected files in the context 132 if self.context is not None: 133 self.context["selected_segmentation_files"] = self.file_selector_widget.get_selected_files() 134 135 # Instantiate the next page only once 136 if not self.next_page: 137 self.next_page = DlExecutionPage(self.context, self) 138 if "history" in self.context: 139 self.context["history"].append(self.next_page) 140 141 # Prepare the next page before displaying 142 self.next_page.on_enter() 143 return self.next_page 144 145 def on_enter(self): 146 """Called when the page is shown — resets transient UI elements.""" 147 self.status_label.setText("") 148 149 def is_ready_to_advance(self): 150 """ 151 Check if the user can proceed to the next page. 152 153 :return: True if one or more files are selected. 154 """ 155 return self.file_selector_widget.get_selected_files() 156 157 def is_ready_to_go_back(self): 158 """ 159 Check if the user can navigate backward. 160 161 :return: Always True for this page. 162 """ 163 return True 164 165 def reset_page(self): 166 """ 167 Reset the page to its initial state, clearing all selections and messages. 168 """ 169 self.file_selector_widget._selected_files = [] 170 171 self.status_label.setText("") 172 173 # Clear any saved data in the context 174 if self.context: 175 self.context["selected_segmentation_files"] = [] 176 177 def _translate_ui(self): 178 """Apply translations to all text elements in the UI.""" 179 self.title.setText(QCoreApplication.translate( 180 "DlSelectionPage", 181 "Select NIfTI files for Deep Learning Segmentation" 182 ))
GUI page that allows the user to select NIfTI files for Deep Learning-based segmentation.
This page is part of a multi-step workflow:
- Displays a title and a FileSelectorWidget to pick one or more NIfTI files.
- Checks if a segmentation already exists for a given subject.
- Saves selections in a shared context and navigates to the next page (DlExecutionPage).
28 def __init__(self, context=None, previous_page=None): 29 """ 30 Initialize the patient selection page. 31 32 :param context: Shared application context dictionary (used for data persistence between pages) 33 :param previous_page: Reference to the previous Page object (for navigation) 34 """ 35 super().__init__() 36 self.context = context 37 self.previous_page = previous_page 38 self.next_page = None # Will be created when advancing 39 40 self._setup_ui() 41 self._translate_ui() 42 43 # Re-translate the UI dynamically if the language changes in the context 44 if context and "language_changed" in context: 45 context["language_changed"].connect(self._translate_ui)
Initialize the patient selection page.
Parameters
- context: Shared application context dictionary (used for data persistence between pages)
- previous_page: Reference to the previous Page object (for navigation)
77 def has_existing_segmentation(self, nifti_file_path, workspace_path): 78 """ 79 Check if a deep learning segmentation already exists for the patient corresponding to this NIfTI file. 80 81 :param nifti_file_path: Full path to the selected NIfTI file. 82 :param workspace_path: Root path of the working directory. 83 :return: True if a segmentation file is already present, otherwise False. 84 """ 85 # Extract patient ID (subject ID) from the NIfTI file path 86 path_parts = nifti_file_path.replace(workspace_path, '').strip(os.sep).split(os.sep) 87 88 # Look for a directory segment starting with 'sub-' (BIDS convention) 89 subject_id = None 90 for part in path_parts: 91 if part.startswith('sub-'): 92 subject_id = part 93 break 94 95 # If no subject identifier found, no segmentation can be associated 96 if not subject_id: 97 return False 98 99 # Expected directory where segmentations are stored 100 seg_dir = os.path.join(workspace_path, 'derivatives', 'deep_learning_seg', subject_id, 'anat') 101 102 # If the segmentation directory does not exist, return False 103 if not os.path.exists(seg_dir): 104 return False 105 106 # Check for any segmentation file in the directory 107 for file in os.listdir(seg_dir): 108 if file.endswith('_seg.nii.gz') or file.endswith('_seg.nii'): 109 return True 110 111 return False
Check if a deep learning segmentation already exists for the patient corresponding to this NIfTI file.
Parameters
- nifti_file_path: Full path to the selected NIfTI file.
- workspace_path: Root path of the working directory.
Returns
True if a segmentation file is already present, otherwise False.
113 def back(self): 114 """ 115 Navigate back to the previous page in the workflow. 116 117 :return: The previous Page instance, or None if there is none. 118 """ 119 if self.previous_page: 120 self.previous_page.on_enter() 121 return self.previous_page 122 return None
Navigate back to the previous page in the workflow.
Returns
The previous Page instance, or None if there is none.
124 def next(self, context): 125 """ 126 Save the selected files in the context and move to the next page (DlExecutionPage). 127 128 :param context: The shared application context dictionary. 129 :return: The next Page instance. 130 """ 131 # Store selected files in the context 132 if self.context is not None: 133 self.context["selected_segmentation_files"] = self.file_selector_widget.get_selected_files() 134 135 # Instantiate the next page only once 136 if not self.next_page: 137 self.next_page = DlExecutionPage(self.context, self) 138 if "history" in self.context: 139 self.context["history"].append(self.next_page) 140 141 # Prepare the next page before displaying 142 self.next_page.on_enter() 143 return self.next_page
Save the selected files in the context and move to the next page (DlExecutionPage).
Parameters
- context: The shared application context dictionary.
Returns
The next Page instance.
145 def on_enter(self): 146 """Called when the page is shown — resets transient UI elements.""" 147 self.status_label.setText("")
Called when the page is shown — resets transient UI elements.
149 def is_ready_to_advance(self): 150 """ 151 Check if the user can proceed to the next page. 152 153 :return: True if one or more files are selected. 154 """ 155 return self.file_selector_widget.get_selected_files()
Check if the user can proceed to the next page.
Returns
True if one or more files are selected.
157 def is_ready_to_go_back(self): 158 """ 159 Check if the user can navigate backward. 160 161 :return: Always True for this page. 162 """ 163 return True
Check if the user can navigate backward.
Returns
Always True for this page.
165 def reset_page(self): 166 """ 167 Reset the page to its initial state, clearing all selections and messages. 168 """ 169 self.file_selector_widget._selected_files = [] 170 171 self.status_label.setText("") 172 173 # Clear any saved data in the context 174 if self.context: 175 self.context["selected_segmentation_files"] = []
Reset the page to its initial state, clearing all selections and messages.