diff --git a/SimpleFilters/SimpleFilters.py b/SimpleFilters/SimpleFilters.py index 20df6ca..09849be 100644 --- a/SimpleFilters/SimpleFilters.py +++ b/SimpleFilters/SimpleFilters.py @@ -206,6 +206,23 @@ def setup(self): hlayout.addWidget(self.applyButton) self.layout.addLayout(hlayout) + # + # Advanced Area + # + advancedCollapsibleButton = ctk.ctkCollapsibleButton() + advancedCollapsibleButton.text = "Advanced" + advancedCollapsibleButton.collapsed = True + self.layout.addWidget(advancedCollapsibleButton) + + # Layout within the advanced collapsible button + advancedFormLayout = qt.QFormLayout(advancedCollapsibleButton) + + self.showOutputCheckbox = qt.QCheckBox() + self.showOutputCheckbox.checked = True + self.showOutputCheckbox.toolTip = "Automatically show output volume in slice views and fit slices to volume after execution is completed." + self.showOutputCheckbox.connect('toggled(bool)', self.onShowOutputCheckboxToggled) + advancedFormLayout.addRow("Auto-show output:", self.showOutputCheckbox) + # connections self.restoreDefaultsButton.connect('clicked(bool)', self.onRestoreDefaultsButton) self.applyButton.connect('clicked(bool)', self.onApplyButton) @@ -290,6 +307,14 @@ def onApplyButton(self): self.filterParameters.prerun() self.logic = SimpleFiltersLogic() + self.logic.showOutput = self.showOutputCheckbox.checked + + if self.filterParameters.outputSelector.currentNode() is None: + # create a new output volume + if self.filterParameters.outputLabelMap: + self.filterParameters.outputSelector.addNode("vtkMRMLLabelMapVolumeNode") + else: + self.filterParameters.outputSelector.addNode("vtkMRMLScalarVolumeNode") self.printPythonCommand() @@ -320,6 +345,11 @@ def onCancelButton(self): self.logic.abort = True + def onShowOutputCheckboxToggled(self, checked): + if self.logic: + self.logic.showOutput = checked + + def onLogicEventStart(self): self.filterStartTime = time.time() self.currentStatusLabel.text = "Running" @@ -365,6 +395,7 @@ def __init__(self): self.main_queue_running = False self.thread = threading.Thread() self.abort = False + self.showOutput = True def __del__(self): @@ -438,13 +469,37 @@ def thread_doit(self,sitkFilter,*inputImages): self.main_queue.put(lambda img=img:self.updateOutput(img)) except Exception as e: - import traceback - traceback.print_exc() if hasattr(e, 'message'): msg = e.message else: msg = str(e) + + # Check if this is a pixel type error and retry with float cast + if re.search(r'Pixel type:.*is not supported', msg): + try: + print("This filter is not compatible with the pixel type of the input images. Attempting to retry filter after casting all input images to float.") + + # Cast all input images to float + floatImages = [sitk.Cast(img, sitk.sitkFloat32) for img in inputImages] + img = sitkFilter.Execute(*floatImages) + + if not self.abort: + self.main_queue.put(lambda img=img:self.updateOutput(img)) + return + except Exception as e2: + import traceback + traceback.print_exc() + if hasattr(e2, 'message'): + msg = e2.message + else: + msg = str(e2) + + else: + # It is some other error, just log it + import traceback + traceback.print_exc() + self.abort = True self.yieldPythonGIL() @@ -502,21 +557,22 @@ def main_queue_process(self): def updateOutput(self,img): node = slicer.mrmlScene.GetNodeByID(self.outputNodeID) - + # Volume is temporarily set to empty during reading from file, pause rendering to avoid warnings with slicer.util.RenderBlocker(): sitkUtils.PushVolumeToSlicer(img, node) - applicationLogic = slicer.app.applicationLogic() - selectionNode = applicationLogic.GetSelectionNode() + if self.showOutput: + applicationLogic = slicer.app.applicationLogic() + selectionNode = applicationLogic.GetSelectionNode() - if self.outputLabelMap: - selectionNode.SetReferenceActiveLabelVolumeID(node.GetID()) - else: - selectionNode.SetReferenceActiveVolumeID(node.GetID()) + if self.outputLabelMap: + selectionNode.SetReferenceActiveLabelVolumeID(node.GetID()) + else: + selectionNode.SetReferenceActiveVolumeID(node.GetID()) - applicationLogic.PropagateVolumeSelection(0) - applicationLogic.FitSliceToAll() + applicationLogic.PropagateVolumeSelection(0) + applicationLogic.FitSliceToAll() def run(self, filter, outputMRMLNode, outputLabelMap, *inputs): """ @@ -864,8 +920,9 @@ def create(self, json): self.outputSelector.showHidden = False self.outputSelector.showChildNodeTypes = False self.outputSelector.baseName = json["name"]+" Output" - self.outputSelector.setMRMLScene( slicer.mrmlScene ) self.outputSelector.setToolTip( "Pick the output to the algorithm." ) + self.outputSelector.noneDisplay = "(Create New Volume)" + self.outputSelector.setMRMLScene( slicer.mrmlScene ) self.outputSelector.connect("nodeActivated(vtkMRMLNode*)", lambda node:self.onOutputSelect(node)) self.widgetConnections.append((self.outputSelector, "nodeActivated(vtkMRMLNode*)")) diff --git a/SimpleFilters/Testing/Python/SimpleFiltersModuleTest.py b/SimpleFilters/Testing/Python/SimpleFiltersModuleTest.py index 387f9f8..a645dbf 100644 --- a/SimpleFilters/Testing/Python/SimpleFiltersModuleTest.py +++ b/SimpleFilters/Testing/Python/SimpleFiltersModuleTest.py @@ -25,7 +25,7 @@ def delayDisplay(self,message,msec=1000): self.info.exec_() # make sure all events are processed before moving on - qt.QApplication.flush() + slicer.app.processEvents() def setUp(self): """ Do whatever is needed to reset the state - typically a scene clear will be enough.