diff --git a/CMakeLists.txt b/CMakeLists.txt index 66b1a95..fd588e5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,21 +22,15 @@ find_package(SlicerDevelopmentToolbox REQUIRED) find_package(DCMQI REQUIRED) find_package(PETDICOMExtension REQUIRED) -set(DEPENDENCY_BUILD_DIRS +set(DEPENDENCIES_ADDITIONAL_MODULE_PATHS + ${SlicerDevelopmentToolbox_DIR}/${Slicer_QTSCRIPTEDMODULES_LIB_DIR} ${SlicerDevelopmentToolbox_DIR}/${Slicer_QTLOADABLEMODULES_LIB_DIR} ${SlicerDevelopmentToolbox_DIR}/${Slicer_CLIMODULES_LIB_DIR} - ${PETDICOMExtension_DIR}/${Slicer_QTLOADABLEMODULES_LIB_DIR} + ${PETDICOMExtension_DIR}/${Slicer_QTSCRIPTEDMODULES_LIB_DIR} ${PETDICOMExtension_DIR}/${Slicer_CLIMODULES_LIB_DIR} ${DCMQI_DIR}/dcmqi-build/bin ) -message("DEPENDENCY_BUILD_DIRS: ${DEPENDENCY_BUILD_DIRS}") - -set(DEPENDENCIES_ADDITIONAL_MODULE_PATHS - ${DEPENDENCY_BUILD_DIRS} - ${SlicerDevelopmentToolbox_DIR}/${Slicer_QTSCRIPTEDMODULES_LIB_DIR} -) - add_subdirectory(DICOMPlugins) add_subdirectory(QuantitativeReporting) diff --git a/QuantitativeReporting/QuantitativeReporting.py b/QuantitativeReporting/QuantitativeReporting.py index f2bfa52..78b898d 100644 --- a/QuantitativeReporting/QuantitativeReporting.py +++ b/QuantitativeReporting/QuantitativeReporting.py @@ -211,10 +211,8 @@ def loadSeriesByFileName(self, filename): self.loadSeries(seriesUID) def loadSeries(self, seriesUID): - dicomWidget = slicer.modules.dicom.widgetRepresentation().self() - dicomWidget.detailsPopup.offerLoadables(seriesUID, 'Series') - dicomWidget.detailsPopup.examineForLoading() - dicomWidget.detailsPopup.loadCheckedLoadables() + from DICOMLib.DICOMUtils import loadSeriesByUID + loadSeriesByUID([seriesUID]) def setupSelectionArea(self): self.segmentEditorWidget.editor.masterVolumeNodeSelectorAddAttribute("vtkMRMLScalarVolumeNode", diff --git a/Testing/CMakeLists.txt b/Testing/CMakeLists.txt index a403586..79c0c36 100644 --- a/Testing/CMakeLists.txt +++ b/Testing/CMakeLists.txt @@ -3,4 +3,9 @@ slicer_add_python_unittest( SLICER_ARGS --additional-module-paths ${CMAKE_BINARY_DIR}/${Slicer_QTSCRIPTEDMODULES_LIB_DIR} ${DEPENDENCIES_ADDITIONAL_MODULE_PATHS} - ) \ No newline at end of file + ) + +slicerMacroBuildScriptedModule( + NAME ${MODULE_NAME}SelfTests + SCRIPTS QuantitativeReportingTests.py +) diff --git a/Testing/QuantitativeReportingTests.py b/Testing/QuantitativeReportingTests.py index 12019db..15da2aa 100644 --- a/Testing/QuantitativeReportingTests.py +++ b/Testing/QuantitativeReportingTests.py @@ -4,6 +4,7 @@ import vtk import slicer import inspect +from DICOMLib import DICOMUtils import vtkSegmentationCorePython as vtkSegmentationCore @@ -36,7 +37,6 @@ def __init__(self, parent): except AttributeError: slicer.selfTests = {} slicer.selfTests['QuantitativeReporting'] = self.runTest - slicer.selfTests['QuantitativeReportingTests'] = self.runTest def runTest(self): tester = QuantitativeReportingTest() @@ -102,6 +102,8 @@ def layoutManager(self): def setUp(self): self.delayDisplay("Closing the scene") + self.dicomDatabaseDir = os.path.join(slicer.app.temporaryPath, 'QuantitativeReporting_SelfTest', 'CtkDicomDatabase') + self.layoutManager.selectModule("QuantitativeReporting") slicer.mrmlScene.Clear(0) self.setupTimer() @@ -121,7 +123,6 @@ def loadTestVolume(self): def runTest(self): """Run as few or as many tests as needed here. """ - for testName in [f for f in QuantitativeReportingTest.__dict__.keys() if f.startswith('test_')]: self.setUp() getattr(self, testName)() @@ -136,46 +137,47 @@ def loadTestData(): sampleData = TestDataLogic.downloadAndUnzipSampleData(self.collection) TestDataLogic.importIntoDICOMDatabase(sampleData[imageType]) - def checkFocusAndClickButton(): - focus = slicer.app.focusWidget() - focus.parent().parent().yesButton.click() + with DICOMUtils.TemporaryDICOMDatabase(self.dicomDatabaseDir) as db: + self.assertTrue(db.isOpen) + self.assertEqual(slicer.dicomDatabase, db) - loadTestData() + loadTestData() - dicomWidget = slicer.modules.dicom.widgetRepresentation().self() - checkbox = dicomWidget.detailsPopup.pluginSelector.checkBoxByPlugin["DICOMLongitudinalTID1500Plugin"] - crntState = checkbox.checked - checkbox.checked = False + # dicomWidget = slicer.modules.dicom.widgetRepresentation().self() + # checkbox = dicomWidget.detailsPopup.pluginSelector.checkBoxByPlugin["DICOMLongitudinalTID1500Plugin"] + # crntState = checkbox.checked + # checkbox.checked = False - timer = qt.QTimer() - timer.setInterval(3000) - timer.setSingleShot(True) - timer.timeout.connect(checkFocusAndClickButton) - timer.start() + qrWidget = slicer.modules.QuantitativeReportingWidget - qrWidget = slicer.modules.QuantitativeReportingWidget - qrWidget.loadSeries(self.data['sr']['uid']) + settings = 'DICOM/automaticallyLoadReferences' + cacheAutoLoadReferences = qt.QSettings().value('DICOM/automaticallyLoadReferences') + qt.QSettings().setValue(settings, qt.QMessageBox.Yes) + + qrWidget.loadSeries(self.data['sr']['uid']) + + qt.QSettings().setValue(settings, cacheAutoLoadReferences) - checkbox.checked = crntState + # checkbox.checked = crntState - tableNodes = slicer.util.getNodesByClass("vtkMRMLTableNode") + tableNodes = slicer.util.getNodesByClass("vtkMRMLTableNode") - self.assertTrue(len(tableNodes), - "Loading SR into mrmlScene failed. No vtkMRMLTableNodes were found within the scene.") + self.assertTrue(len(tableNodes), + "Loading SR into mrmlScene failed. No vtkMRMLTableNodes were found within the scene.") - self.delayDisplay('Selecting measurements report') + self.delayDisplay('Selecting measurements report') - qrWidget.measurementReportSelector.setCurrentNode(tableNodes[0]) + qrWidget.measurementReportSelector.setCurrentNode(tableNodes[0]) - self.delayDisplay('Checking number of segments') - self.assertTrue(len(qrWidget.segmentEditorWidget.segments) == 3, - "Number of segments does not match expected count of 3") + self.delayDisplay('Checking number of segments') + self.assertTrue(len(qrWidget.segmentEditorWidget.segments) == 3, + "Number of segments does not match expected count of 3") - self.delayDisplay('Checking referenced master volume', 2000) - self.assertIsNotNone(qrWidget.segmentEditorWidget.masterVolumeNode, - "Master volume for the selected measurement report is None!") + self.delayDisplay('Checking referenced master volume', 2000) + self.assertIsNotNone(qrWidget.segmentEditorWidget.masterVolumeNode, + "Master volume for the selected measurement report is None!") - self.delayDisplay('Test passed!') + self.delayDisplay('Test passed!') def test_create_report(self): @@ -183,82 +185,79 @@ def test_create_report(self): qrWidget = slicer.modules.QuantitativeReportingWidget - self.loadTestVolume() - success, err = qrWidget.saveReport() - self.assertFalse(success) + with DICOMUtils.TemporaryDICOMDatabase(self.dicomDatabaseDir) as db: + self.assertTrue(db.isOpen) + self.assertEqual(slicer.dicomDatabase, db) - self.delayDisplay('Add segments') + self.loadTestVolume() + success, err = qrWidget.saveReport() + self.assertFalse(success) - qrWidget = slicer.modules.QuantitativeReportingWidget - segmentation = qrWidget.segmentEditorWidget.segmentationNode.GetSegmentation() + self.delayDisplay('Add segments') - segmentGeometries = { - 'Tumor': [[2, 30, 30, -127.7], [2, 40, 40, -127.7], [2, 50, 50, -127.7], [2, 40, 80, -127.7]], - 'Air': [[2, 60, 100, -127.7], [2, 80, 30, -127.7]] - } + qrWidget = slicer.modules.QuantitativeReportingWidget + segmentation = qrWidget.segmentEditorWidget.segmentationNode.GetSegmentation() + + segmentGeometries = { + 'Tumor': [[2, 30, 30, -127.7], [2, 40, 40, -127.7], [2, 50, 50, -127.7], [2, 40, 80, -127.7]], + 'Air': [[2, 60, 100, -127.7], [2, 80, 30, -127.7]] + } - for segmentName, segmentGeometry in segmentGeometries.iteritems(): - appender = vtk.vtkAppendPolyData() + for segmentName, segmentGeometry in segmentGeometries.iteritems(): + appender = vtk.vtkAppendPolyData() - for sphere in segmentGeometry: - sphereSource = vtk.vtkSphereSource() - sphereSource.SetRadius(sphere[0]) - sphereSource.SetCenter(sphere[1], sphere[2], sphere[3]) - appender.AddInputConnection(sphereSource.GetOutputPort()) + for sphere in segmentGeometry: + sphereSource = vtk.vtkSphereSource() + sphereSource.SetRadius(sphere[0]) + sphereSource.SetCenter(sphere[1], sphere[2], sphere[3]) + appender.AddInputConnection(sphereSource.GetOutputPort()) - segment = vtkSegmentationCore.vtkSegment() - segment.SetName(segmentation.GenerateUniqueSegmentID(segmentName)) + segment = vtkSegmentationCore.vtkSegment() + segment.SetName(segmentation.GenerateUniqueSegmentID(segmentName)) - appender.Update() - representationName = vtkSegmentationCore.vtkSegmentationConverter.GetSegmentationClosedSurfaceRepresentationName() - segment.AddRepresentation(representationName, appender.GetOutput()) - segmentation.AddSegment(segment) + appender.Update() + representationName = vtkSegmentationCore.vtkSegmentationConverter.GetSegmentationClosedSurfaceRepresentationName() + segment.AddRepresentation(representationName, appender.GetOutput()) + segmentation.AddSegment(segment) - self.delayDisplay('Save report') + self.delayDisplay('Save report') - success, err = qrWidget.saveReport() - self.assertTrue(success) + success, err = qrWidget.saveReport() + self.assertTrue(success) - self.delayDisplay('Test passed!') + self.delayDisplay('Test passed!') def test_import_labelmap(self): self.delayDisplay('Starting %s' % inspect.stack()[0][3]) qrWidget = slicer.modules.QuantitativeReportingWidget - self.loadTestVolume() - sampleData = TestDataLogic.downloadAndUnzipSampleData(self.collection) - segmentationsDir = sampleData['seg_nrrd'] + with DICOMUtils.TemporaryDICOMDatabase(self.dicomDatabaseDir) as db: + self.assertTrue(db.isOpen) + self.assertEqual(slicer.dicomDatabase, db) - labels = [] - for f in [os.path.join(segmentationsDir, f) for f in os.listdir(segmentationsDir) if f.endswith(".nrrd")]: - _, label = slicer.util.loadVolume(f, {'labelmap': True}, returnNode=True) - if label: - labels.append(label) + self.loadTestVolume() - labelImportWidget = qrWidget.labelMapImportWidget - - timer = qt.QTimer() - timer.setInterval(2000) - timer.timeout.connect(self._checkFocusAndClickButton) - timer.start() + sampleData = TestDataLogic.downloadAndUnzipSampleData(self.collection) + segmentationsDir = sampleData['seg_nrrd'] - for label in labels: - labelImportWidget.labelMapSelector.setCurrentNode(label) - labelImportWidget.importButton.click() + labels = [] + for f in [os.path.join(segmentationsDir, f) for f in os.listdir(segmentationsDir) if f.endswith(".nrrd")]: + _, label = slicer.util.loadVolume(f, {'labelmap': True}, returnNode=True) + if label: + labels.append(label) - timer.stop() + labelImportLogic = qrWidget.labelMapImportWidget.logic - segmentation = qrWidget.segmentEditorWidget.segmentationNode.GetSegmentation() - self.assertEquals(segmentation.GetNumberOfSegments(), len(labels)) + for label in labels: + labelImportLogic.labelmap = label + labelImportLogic.run(resampleIfNecessary=True) - self.delayDisplay('Test passed!') + segmentation = qrWidget.segmentEditorWidget.segmentationNode.GetSegmentation() + self.assertEquals(segmentation.GetNumberOfSegments(), len(labels)) - def _checkFocusAndClickButton(self): - focus = slicer.app.focusWidget() - if type(focus) is qt.QPushButton: - focus.click() + self.delayDisplay('Test passed!') def test_import_segmentation(self): @@ -266,42 +265,43 @@ def test_import_segmentation(self): uid = self.data["seg_dcm"]["uid"] - self.loadTestVolume() + with DICOMUtils.TemporaryDICOMDatabase(self.dicomDatabaseDir) as db: + self.assertTrue(db.isOpen) + self.assertEqual(slicer.dicomDatabase, db) - if not len(slicer.dicomDatabase.filesForSeries(uid)): - sampleData = TestDataLogic.downloadAndUnzipSampleData(self.collection) - TestDataLogic.importIntoDICOMDatabase(sampleData["seg_dcm"]) + self.loadTestVolume() - def checkFocusAndClickButton(): - focus = slicer.app.focusWidget() - focus.parent().parent().noButton.click() + if not len(slicer.dicomDatabase.filesForSeries(uid)): + sampleData = TestDataLogic.downloadAndUnzipSampleData(self.collection) + TestDataLogic.importIntoDICOMDatabase(sampleData["seg_dcm"]) - timer = qt.QTimer() - timer.setInterval(3000) - timer.setSingleShot(True) - timer.timeout.connect(checkFocusAndClickButton) - timer.start() + qrWidget = slicer.modules.QuantitativeReportingWidget - qrWidget = slicer.modules.QuantitativeReportingWidget - qrWidget.loadSeries(uid) + settings = 'DICOM/automaticallyLoadReferences' + cacheAutoLoadReferences = qt.QSettings().value('DICOM/automaticallyLoadReferences') + qt.QSettings().setValue(settings, qt.QMessageBox.Yes) + + qrWidget.loadSeries(uid) + + qt.QSettings().setValue(settings, cacheAutoLoadReferences) - segmentationNode = slicer.util.getNodesByClass('vtkMRMLSegmentationNode')[-1] + segmentationNode = slicer.util.getNodesByClass('vtkMRMLSegmentationNode')[-1] - qrWidget.importSegmentationCollapsibleButton.collapsed = False + qrWidget.importSegmentationCollapsibleButton.collapsed = False - importWidget = qrWidget.segmentImportWidget - importWidget.otherSegmentationNodeSelector.setCurrentNode(segmentationNode) + importWidget = qrWidget.segmentImportWidget + importWidget.otherSegmentationNodeSelector.setCurrentNode(segmentationNode) - segmentIDs = qrWidget.segmentEditorWidget.logic.getSegmentIDs(segmentationNode, False) - importWidget.otherSegmentsTableView.setSelectedSegmentIDs(segmentIDs) + segmentIDs = qrWidget.segmentEditorWidget.logic.getSegmentIDs(segmentationNode, False) + importWidget.otherSegmentsTableView.setSelectedSegmentIDs(segmentIDs) - importWidget.copyOtherToCurrentButton.click() + importWidget.copyOtherToCurrentButton.click() - self.delayDisplay('Checking number of imported segments') - self.assertTrue(len(qrWidget.segmentEditorWidget.segments) == 3, - "Number of segments does not match expected count of 3") + self.delayDisplay('Checking number of imported segments') + self.assertTrue(len(qrWidget.segmentEditorWidget.segments) == 3, + "Number of segments does not match expected count of 3") - self.delayDisplay('Test passed!') + self.delayDisplay('Test passed!') def _selectModule(self): self.layoutManager.selectModule("QuantitativeReporting") \ No newline at end of file