Ejemplo n.º 1
0
    def setup(self):

        #
        # servers
        #

        # testing server - not exposed (used for development)
        self.localFrame = ctk.ctkCollapsibleButton(self.parent)
        self.localFrame.setLayout(qt.QVBoxLayout())
        self.localFrame.setText("Servers")
        self.layout.addWidget(self.localFrame)
        self.localFrame.collapsed = False

        self.toggleServer = qt.QPushButton("Start Testing Server")
        self.localFrame.layout().addWidget(self.toggleServer)
        self.toggleServer.connect('clicked()', self.onToggleServer)

        self.verboseServer = qt.QCheckBox("Verbose")
        self.localFrame.layout().addWidget(self.verboseServer)

        # advanced options - not exposed to end users
        # developers can uncomment these lines to access testing server
        self.toggleServer.hide()
        self.verboseServer.hide()

        # Listener

        settings = qt.QSettings()
        self.toggleListener = qt.QPushButton()
        self.toggleListener.checkable = True
        if hasattr(slicer, 'dicomListener'):
            self.toggleListener.text = "Stop Listener"
            slicer.dicomListener.process.connect('stateChanged(int)',
                                                 self.onListenerStateChanged)
        else:
            self.toggleListener.text = "Start Listener"
        self.localFrame.layout().addWidget(self.toggleListener)
        self.toggleListener.connect('clicked()', self.onToggleListener)

        self.runListenerAtStart = qt.QCheckBox(
            "Start Listener when Slicer Starts")
        self.localFrame.layout().addWidget(self.runListenerAtStart)
        self.runListenerAtStart.checked = settingsValue(
            'DICOM/RunListenerAtStart', False, converter=toBool)
        self.runListenerAtStart.connect('clicked()', self.onRunListenerAtStart)

        # the Database frame (home of the ctkDICOM widget)
        self.dicomFrame = ctk.ctkCollapsibleButton(self.parent)
        self.dicomFrame.setLayout(qt.QVBoxLayout())
        self.dicomFrame.setText("DICOM Database and Networking")
        self.layout.addWidget(self.dicomFrame)

        self.detailsPopup = DICOMLib.DICOMDetailsPopup()

        # XXX Slicer 4.5 - Remove these. Here only for backward compatibility.
        self.dicomBrowser = self.detailsPopup.dicomBrowser
        self.tables = self.detailsPopup.tables

        # connect to the 'Show DICOM Browser' button
        self.showBrowserButton = qt.QPushButton('Show DICOM Browser')
        self.dicomFrame.layout().addWidget(self.showBrowserButton)
        self.showBrowserButton.connect('clicked()', self.detailsPopup.open)

        # connect to the main window's dicom button
        mw = slicer.util.mainWindow()
        if mw:
            try:
                action = slicer.util.findChildren(mw,
                                                  name='LoadDICOMAction')[0]
                action.connect('triggered()', self.detailsPopup.open)
            except IndexError:
                logging.error(
                    'Could not connect to the main window DICOM button')

        if hasattr(slicer, 'dicomListener'):
            slicer.dicomListener.fileToBeAddedCallback = self.onListenerToAddFile
            slicer.dicomListener.fileAddedCallback = self.onListenerAddedFile

        slicer.dicomDatabase.connect('databaseChanged()',
                                     self.onDatabaseChanged)

        # the recent activity frame
        self.activityFrame = ctk.ctkCollapsibleButton(self.parent)
        self.activityFrame.setLayout(qt.QVBoxLayout())
        self.activityFrame.setText("Recent DICOM Activity")
        self.layout.addWidget(self.activityFrame)

        self.recentActivity = DICOMLib.DICOMRecentActivityWidget(
            self.activityFrame, detailsPopup=self.detailsPopup)
        self.activityFrame.layout().addWidget(self.recentActivity.widget)
        self.requestUpdateRecentActivity()

        # Add spacer to layout
        self.layout.addStretch(1)
Ejemplo n.º 2
0
  def setup(self):

    #
    # servers
    #

    # testing server - not exposed (used for development)
    self.localFrame = ctk.ctkCollapsibleButton(self.parent)
    self.localFrame.setLayout(qt.QVBoxLayout())
    self.localFrame.setText("Servers")
    self.layout.addWidget(self.localFrame)
    self.localFrame.collapsed = False

    self.toggleServer = qt.QPushButton("Start Testing Server")
    self.localFrame.layout().addWidget(self.toggleServer)
    self.toggleServer.connect('clicked()', self.onToggleServer)

    self.verboseServer = qt.QCheckBox("Verbose")
    self.localFrame.layout().addWidget(self.verboseServer)

    # advanced options - not exposed to end users
    # developers can uncomment these lines to access testing server
    self.toggleServer.hide()
    self.verboseServer.hide()

    # Listener

    settings = qt.QSettings()
    self.toggleListener = qt.QPushButton()
    if hasattr(slicer, 'dicomListener'):
      self.toggleListener.text = "Stop Listener"
      slicer.dicomListener.process.connect('stateChanged(int)',self.onListenerStateChanged)
    else:
      self.toggleListener.text = "Start Listener"
    self.localFrame.layout().addWidget(self.toggleListener)
    self.toggleListener.connect('clicked()', self.onToggleListener)

    self.runListenerAtStart = qt.QCheckBox("Start Listener when Slicer Starts")
    self.localFrame.layout().addWidget(self.runListenerAtStart)
    if settings.contains('DICOM/RunListenerAtStart'):
      self.runListenerAtStart.checked = bool(settings.value('DICOM/RunListenerAtStart'))
    self.runListenerAtStart.connect('clicked()', self.onRunListenerAtStart)

    # the Database frame (home of the ctkDICOM widget)
    self.dicomFrame = ctk.ctkCollapsibleButton(self.parent)
    self.dicomFrame.setLayout(qt.QVBoxLayout())
    self.dicomFrame.setText("DICOM Database and Networking")
    self.layout.addWidget(self.dicomFrame)

    # initialize the dicomDatabase
    #   - pick a default and let the user know
    if not slicer.dicomDatabase:
      self.promptForDatabaseDirectory()

    #
    # create and configure the app widget - this involves
    # reaching inside and manipulating the widget hierarchy
    # - TODO: this configurability should be exposed more natively
    #   in the CTK code to avoid the findChildren calls
    #
    self.dicomApp = ctk.ctkDICOMAppWidget()
    DICOM.setDatabasePrecacheTags(self.dicomApp)
    if self.hideSearch:
      # hide the search options - doesn't work yet and doesn't fit
      # well into the frame
      slicer.util.findChildren(self.dicomApp, 'SearchOption')[0].hide()

    self.detailsPopup = DICOMLib.DICOMDetailsPopup(self.dicomApp,setBrowserPersistence=self.setBrowserPersistence)

    self.tree = self.detailsPopup.tree

    self.showBrowser = qt.QPushButton('Show DICOM Browser')
    self.dicomFrame.layout().addWidget(self.showBrowser)
    self.showBrowser.connect('clicked()', self.detailsPopup.open)

    # connect to the main window's dicom button
    mw = slicer.util.mainWindow()
    try:
      action = slicer.util.findChildren(mw,name='actionLoadDICOM')[0]
      action.connect('triggered()',self.detailsPopup.open)
    except IndexError:
      print('Could not connect to the main window DICOM button')

    # connect to our menu file entry so it raises the browser
    menuFile = slicer.util.lookupTopLevelWidget('menuFile')
    if menuFile:
      for action in menuFile.actions():
        if action.text == 'DICOM':
          action.connect('triggered()',self.detailsPopup.open)

    # make the tree view a bit bigger
    self.tree.setMinimumHeight(250)

    if hasattr(slicer, 'dicomListener'):
      slicer.dicomListener.fileToBeAddedCallback = self.onListenerToAddFile
      slicer.dicomListener.fileAddedCallback = self.onListenerAddedFile

    self.contextMenu = qt.QMenu(self.tree)
    self.exportAction = qt.QAction("Export to Study", self.contextMenu)
    self.contextMenu.addAction(self.exportAction)
    self.exportAction.enabled = False
    self.deleteAction = qt.QAction("Delete", self.contextMenu)
    self.contextMenu.addAction(self.deleteAction)
    self.contextMenu.connect('triggered(QAction*)', self.onContextMenuTriggered)

    slicer.dicomDatabase.connect('databaseChanged()', self.onDatabaseChanged)
    self.dicomApp.connect('databaseDirectoryChanged(QString)', self.onDatabaseDirectoryChanged)
    selectionModel = self.tree.selectionModel()
    # TODO: can't use this because QList<QModelIndex> is not visible in PythonQt
    #selectionModel.connect('selectionChanged(QItemSelection, QItemSelection)', self.onTreeSelectionChanged)
    self.tree.connect('clicked(QModelIndex)', self.onTreeClicked)
    self.tree.setContextMenuPolicy(3)
    self.tree.connect('customContextMenuRequested(QPoint)', self.onTreeContextMenuRequested)

    # enable to the Send button of the app widget and take it over
    # for our purposes - TODO: fix this to enable it at the ctkDICOM level
    self.sendButton = slicer.util.findChildren(self.dicomApp, text='Send')[0]
    self.sendButton.enabled = False
    self.sendButton.connect('triggered()', self.onSendClicked)

    # the recent activity frame
    self.activityFrame = ctk.ctkCollapsibleButton(self.parent)
    self.activityFrame.setLayout(qt.QVBoxLayout())
    self.activityFrame.setText("Recent DICOM Activity")
    self.layout.addWidget(self.activityFrame)

    self.recentActivity = DICOMLib.DICOMRecentActivityWidget(self.activityFrame,detailsPopup=self.detailsPopup)
    self.activityFrame.layout().addWidget(self.recentActivity.widget)
    self.requestUpdateRecentActivity()


    # Add spacer to layout
    self.layout.addStretch(1)
Ejemplo n.º 3
0
    def test_Part1DICOM(self):
        """ Test the DICOM part of the test using the head atlas
    """

        import os
        self.delayDisplay("Starting the DICOM test")
        #
        # first, get the data - a zip file of dicom data
        #
        import urllib
        downloads = (('http://slicer.kitware.com/midas3/download?items=18822',
                      'Dcmtk-db.zip'), )

        self.delayDisplay("Downloading")
        for url, name in downloads:
            filePath = slicer.app.temporaryPath + '/' + name
            if not os.path.exists(filePath) or os.stat(filePath).st_size == 0:
                self.delayDisplay('Requesting download %s from %s...\n' %
                                  (name, url))
                urllib.urlretrieve(url, filePath)
        self.delayDisplay('Finished with download\n')

        self.delayDisplay("Unzipping")
        dicomFilesDirectory = slicer.app.temporaryPath + '/dicomFiles'
        qt.QDir().mkpath(dicomFilesDirectory)
        slicer.app.applicationLogic().Unzip(filePath, dicomFilesDirectory)

        try:
            self.delayDisplay("Switching to temp database directory")
            tempDatabaseDirectory = slicer.app.temporaryPath + '/tempDICOMDatbase'
            qt.QDir().mkpath(tempDatabaseDirectory)
            if slicer.dicomDatabase:
                originalDatabaseDirectory = os.path.split(
                    slicer.dicomDatabase.databaseFilename)[0]
            else:
                originalDatabaseDirectory = None
                settings = qt.QSettings()
                settings.setValue('DatabaseDirectory', tempDatabaseDirectory)
            dicomWidget = slicer.modules.dicom.widgetRepresentation().self()
            dicomWidget.onDatabaseDirectoryChanged(tempDatabaseDirectory)

            self.delayDisplay('Start Local DICOM Q/R SCP')
            import subprocess
            import os
            configFilePath = dicomFilesDirectory + '/Dcmtk-db/dcmqrscp.cfg'
            processCurrentPath = dicomFilesDirectory + '/Dcmtk-db/'

            dcmqrscpExeOptions = (
                '/bin',
                '/../CTK-build/CMakeExternals/Install/bin',
                '/../DCMTK-install/bin',
                '/../DCMTK-build/bin',
            )

            dcmqrscpExePath = None
            dcmqrscpExeName = '/dcmqrscp'
            if slicer.app.os == 'win':
                dcmqrscpExeName = dcmqrscpExeName + '.exe'
            for path in dcmqrscpExeOptions:
                testPath = slicer.app.slicerHome + path + dcmqrscpExeName
                if os.path.exists(testPath):
                    dcmqrscpExePath = testPath
                    break
            if not dcmqrscpExePath:
                raise (UserWarning("Could not find dcmqrscp executable"))

            args = (dcmqrscpExePath, '-c', configFilePath)
            popen = subprocess.Popen(args,
                                     stdout=subprocess.PIPE,
                                     cwd=processCurrentPath)

            self.delayDisplay('Retrieve DICOM')
            mainWindow = slicer.util.mainWindow()
            mainWindow.moduleSelector().selectModule('DICOM')
            dicomRetrieve = ctk.ctkDICOMRetrieve()
            dicomRetrieve.setKeepAssociationOpen(True)
            dicomRetrieve.setDatabase(slicer.dicomDatabase)
            dicomRetrieve.setCallingAETitle('SlicerAE')
            dicomRetrieve.setCalledAETitle('DCMTK')
            dicomRetrieve.setPort(12345)
            dicomRetrieve.setHost('localhost')
            dicomRetrieve.getStudy(
                '1.2.124.113932.1.170.223.162.178.20050502.160340.12640015')
            popen.kill()
            dicomWidget.detailsPopup.open()
            # click on the first row of the tree
            index = dicomWidget.tree.indexAt(qt.QPoint(0, 0))
            dicomWidget.onTreeClicked(index)

            self.delayDisplay('Loading Selection')
            dicomWidget.detailsPopup.loadCheckedLoadables()

            self.delayDisplay('Change Level')
            layoutManager = slicer.app.layoutManager()
            redWidget = layoutManager.sliceWidget('Red')
            self.clickAndDrag(redWidget, start=(10, 10), end=(10, 40))

            self.delayDisplay('Change Window')
            self.clickAndDrag(redWidget, start=(10, 10), end=(40, 10))

            self.delayDisplay('Change Layout')
            layoutManager = slicer.app.layoutManager()
            layoutManager.setLayout(
                slicer.vtkMRMLLayoutNode.SlicerLayoutOneUpRedSliceView)

            self.delayDisplay('Zoom')
            self.clickAndDrag(redWidget,
                              button='Right',
                              start=(10, 10),
                              end=(10, 40))

            self.delayDisplay('Pan')
            self.clickAndDrag(redWidget,
                              button='Middle',
                              start=(10, 10),
                              end=(40, 40))

            self.delayDisplay('Center')
            redWidget.sliceController().fitSliceToBackground()

            self.delayDisplay('Lightbox')
            redWidget.sliceController().setLightboxTo6x6()

            self.delayDisplay('Conventional Layout')
            layoutManager.setLayout(
                slicer.vtkMRMLLayoutNode.SlicerLayoutConventionalView)

            self.delayDisplay('No Lightbox')
            redWidget.sliceController().setLightboxTo1x1()

            self.delayDisplay('Four Up Layout')
            layoutManager.setLayout(
                slicer.vtkMRMLLayoutNode.SlicerLayoutFourUpView)

            self.delayDisplay('Shift Mouse')
            self.clickAndDrag(redWidget,
                              button='None',
                              start=(100, 100),
                              end=(140, 140),
                              modifiers=['Shift'])

            self.delayDisplay('Conventional, Link, Slice Model')
            layoutManager.setLayout(
                slicer.vtkMRMLLayoutNode.SlicerLayoutConventionalView)
            redWidget.sliceController().setSliceLink(True)
            redWidget.sliceController().setSliceVisible(True)

            self.delayDisplay('Rotate')
            threeDView = layoutManager.threeDWidget(0).threeDView()
            self.clickAndDrag(threeDView)

            self.delayDisplay('Zoom')
            threeDView = layoutManager.threeDWidget(0).threeDView()
            self.clickAndDrag(threeDView, button='Right')

            self.delayDisplay('Test passed!')
        except Exception, e:
            import traceback
            traceback.print_exc()
            self.delayDisplay('Test caused exception!\n' + str(e))
Ejemplo n.º 4
0
    def test_LabelToDICOMSEGConverterRoundTrip(self):
        print("CTEST_FULL_OUTPUT")

        dir(slicer.modules)
        """ Load the data using DICOM module
    """

        import os
        self.delayDisplay("Starting the DICOM test")
        #
        # first, get the data - a zip file of dicom data
        #
        import urllib
        downloads = (('http://slicer.kitware.com/midas3/download?items=10881',
                      'JANCT-CT.zip'), )

        self.delayDisplay("Downloading")
        for url, name in downloads:
            filePath = slicer.app.temporaryPath + '/' + name
            if not os.path.exists(filePath) or os.stat(filePath).st_size == 0:
                self.delayDisplay('Requesting download %s from %s...\n' %
                                  (name, url))
                urllib.urlretrieve(url, filePath)
        self.delayDisplay('Finished with download\n')

        reportingTempDir = slicer.app.temporaryPath + '/LabelToDICOMSEGConverter'
        qt.QDir().mkpath(reportingTempDir)
        dicomFilesDirectory = reportingTempDir + '/dicomFiles'
        self.cleanupDir(dicomFilesDirectory)
        qt.QDir().mkpath(dicomFilesDirectory)
        slicer.app.applicationLogic().Unzip(filePath, dicomFilesDirectory)

        try:
            self.delayDisplay("Switching to temp database directory")
            tempDatabaseDirectory = reportingTempDir + '/tempDICOMDatbase'
            qt.QDir().mkpath(tempDatabaseDirectory)
            self.cleanupDir(tempDatabaseDirectory)
            if slicer.dicomDatabase:
                self.originalDatabaseDirectory = os.path.split(
                    slicer.dicomDatabase.databaseFilename)[0]
            else:
                self.originalDatabaseDirectory = None
                settings = qt.QSettings()
                settings.setValue('DatabaseDirectory', tempDatabaseDirectory)
            dicomWidget = slicer.modules.dicom.widgetRepresentation().self()
            dicomWidget.onDatabaseDirectoryChanged(tempDatabaseDirectory)

            self.delayDisplay('Importing DICOM')
            mainWindow = slicer.util.mainWindow()
            mainWindow.moduleSelector().selectModule('DICOM')

            self.importDICOM(slicer.dicomDatabase, dicomFilesDirectory)

            patient = slicer.dicomDatabase.patients()[0]
            studies = slicer.dicomDatabase.studiesForPatient(patient)
            series = [
                slicer.dicomDatabase.seriesForStudy(study) for study in studies
            ]
            seriesUIDs = [uid for uidList in series for uid in uidList]
            dicomWidget.detailsPopup.offerLoadables(seriesUIDs,
                                                    'SeriesUIDList')
            dicomWidget.detailsPopup.examineForLoading()

            loadablesByPlugin = dicomWidget.detailsPopup.loadablesByPlugin

            self.delayDisplay('Loading Selection')
            dicomWidget.detailsPopup.loadCheckedLoadables()

            # initialize the module with the report and volume

            volumes = slicer.util.getNodes('vtkMRMLScalarVolumeNode*')
            self.assertTrue(len(volumes) == 1)

            (name, volume) = volumes.items()[0]
            self.delayDisplay('Loaded volume name %s' % volume.GetName())

            self.delayDisplay('Configure Module')
            mainWindow = slicer.util.mainWindow()
            mainWindow.moduleSelector().selectModule(
                'LabelToDICOMSEGConverter')

            module = slicer.modules.labeltodicomsegconverter.widgetRepresentation(
            ).self()

            # add label node
            volumesLogic = slicer.modules.volumes.logic()
            labelNode = volumesLogic.CreateAndAddLabelVolume(
                slicer.mrmlScene, volume, "Segmentation")
            labelNode.SetAttribute('AssociatedNodeID', volume.GetID())
            labelDisplayNode = labelNode.GetDisplayNode()
            labelDisplayNode.SetAndObserveColorNodeID(
                'vtkMRMLColorTableNodeFileGenericAnatomyColors.txt')
            image = volume.GetImageData()
            thresh = vtk.vtkImageThreshold()
            if vtk.vtkVersion().GetVTKMajorVersion() < 6:
                thresh.SetInput(image)
            else:
                thresh.SetInputData(image)
            thresh.ThresholdBetween(10, 400)
            thresh.SetInValue(10)
            thresh.SetOutValue(0)
            thresh.Update()
            labelNode.SetAndObserveImageData(thresh.GetOutput())
            module.segmentationSelector.setCurrentNode(labelNode)
            module.volumeSelector.setCurrentNode(volume)

            self.delayDisplay('Input label initialized')

            module.outputDir = reportingTempDir + '/Output'

            # Save the report

            exportDir = reportingTempDir + '/Output'
            qt.QDir().mkpath(exportDir)
            self.cleanupDir(exportDir)
            module.onLabelExport()

            self.delayDisplay('Report saved')

            self.importDICOM(slicer.dicomDatabase, exportDir)

            slicer.mrmlScene.Clear(0)

            patient = slicer.dicomDatabase.patients()[0]
            studies = slicer.dicomDatabase.studiesForPatient(patient)
            series = [
                slicer.dicomDatabase.seriesForStudy(study) for study in studies
            ]
            seriesUIDs = [uid for uidList in series for uid in uidList]
            dicomWidget.detailsPopup.offerLoadables(seriesUIDs,
                                                    'SeriesUIDList')
            dicomWidget.detailsPopup.examineForLoading()

            loadablesByPlugin = dicomWidget.detailsPopup.loadablesByPlugin

            self.delayDisplay('Wait', 10000)

            dicomWidget.detailsPopup.loadCheckedLoadables()
            volumes = slicer.util.getNodes('vtkMRMLLabelMapVolumeNode*')
            for n, v in volumes.items():
                print('Label volume found: ' + v.GetID())
            self.assertTrue(len(volumes) == 1)

            for name, volume in volumes.items():
                image = volume.GetImageData()
                previousImage = thresh.GetOutput()
                diff = vtk.vtkImageDifference()
                if vtk.vtkVersion().GetVTKMajorVersion() < 6:
                    diff.SetInput(thresh.GetOutput())
                else:
                    diff.SetInputData(thresh.GetOutput())
                diff.SetImage(image)
                diff.Update()
                if diff.GetThresholdedError() > 1:
                    self.delayDisplay('Reloaded image does not match')
                    self.assertTrue(False)

            self.delayDisplay('Test passed')

            self.delayDisplay("Restoring original database directory")
            if self.originalDatabaseDirectory:
                dicomWidget.onDatabaseDirectoryChanged(
                    self.originalDatabaseDirectory)

        except Exception, e:
            if self.originalDatabaseDirectory:
                dicomWidget.onDatabaseDirectoryChanged(
                    self.originalDatabaseDirectory)
            import traceback
            traceback.print_exc()
            self.delayDisplay('Test caused exception!\n' + str(e))
            self.assertTrue(False)
Ejemplo n.º 5
0
    def test_Part1DICOM(self,
                        enableScreenshotsFlag=0,
                        screenshotScaleFactor=1):
        """ Test the DICOM part of the test using the head atlas
    """

        self.enableScreenshots = enableScreenshotsFlag
        self.screenshotScaleFactor = screenshotScaleFactor

        import os
        self.delayDisplay("Starting the DICOM test")
        #
        # first, get the data - a zip file of dicom data
        #
        import urllib
        downloads = (('http://slicer.kitware.com/midas3/download?items=124183',
                      'dataset1_Thorax_Abdomen.zip'), )

        self.delayDisplay("Downloading")
        for url, name in downloads:
            filePath = slicer.app.temporaryPath + '/' + name
            if not os.path.exists(filePath) or os.stat(filePath).st_size == 0:
                self.delayDisplay('Requesting download %s from %s...\n' %
                                  (name, url))
                urllib.urlretrieve(url, filePath)
        self.delayDisplay('Finished with download\n')

        self.delayDisplay("Unzipping")
        dicomFilesDirectory = slicer.app.temporaryPath + '/dicomFiles'
        qt.QDir().mkpath(dicomFilesDirectory)
        slicer.app.applicationLogic().Unzip(filePath, dicomFilesDirectory)

        try:
            self.delayDisplay("Switching to temp database directory")
            tempDatabaseDirectory = slicer.app.temporaryPath + '/tempDICOMDatabase'
            qt.QDir().mkpath(tempDatabaseDirectory)
            if slicer.dicomDatabase:
                originalDatabaseDirectory = os.path.split(
                    slicer.dicomDatabase.databaseFilename)[0]
            else:
                originalDatabaseDirectory = None
                settings = qt.QSettings()
                settings.setValue('DatabaseDirectory', tempDatabaseDirectory)
            dicomWidget = slicer.modules.dicom.widgetRepresentation().self()
            dicomWidget.onDatabaseDirectoryChanged(tempDatabaseDirectory)

            self.delayDisplay('Importing DICOM')
            mainWindow = slicer.util.mainWindow()
            mainWindow.moduleSelector().selectModule('DICOM')

            indexer = ctk.ctkDICOMIndexer()
            indexer.addDirectory(slicer.dicomDatabase, dicomFilesDirectory,
                                 None)
            indexer.waitForImportFinished()

            dicomWidget.detailsPopup.open()

            # load the data by series UID
            dicomWidget.detailsPopup.offerLoadables(
                '1.3.12.2.1107.5.1.4.50025.30000005060811542834300000776',
                'Series')
            dicomWidget.detailsPopup.examineForLoading()

            self.delayDisplay('Loading Selection')
            dicomWidget.detailsPopup.loadCheckedLoadables()

            self.takeScreenshot('LoadingADICOMVolume-Loaded',
                                'Loaded DICOM Volume', -1)

            layoutManager = slicer.app.layoutManager()
            redWidget = layoutManager.sliceWidget('Red')
            self.clickAndDrag(redWidget, start=(10, 10), end=(10, 40))
            self.clickAndDrag(redWidget, start=(10, 10), end=(40, 10))

            self.takeScreenshot('LoadingADICOMVolume-WL',
                                'Changed level and window', -1)

            redWidget.sliceController().setSliceLink(True)
            redWidget.sliceController().setSliceVisible(True)
            self.takeScreenshot('LoadingADICOMVolume-LinkView',
                                'Linked and visible', -1)

            self.clickAndDrag(redWidget,
                              button='Right',
                              start=(10, 10),
                              end=(10, 40))
            self.takeScreenshot('LoadingADICOMVolume-Zoom', 'Zoom', -1)

            threeDView = layoutManager.threeDWidget(0).threeDView()
            self.clickAndDrag(threeDView)
            self.takeScreenshot('LoadingADICOMVolume-Rotate', 'Rotate', -1)

            threeDView.resetFocalPoint()
            self.takeScreenshot('LoadingADICOMVolume-Center',
                                'Center the view', -1)

            layoutManager.setLayout(slicer.vtkMRMLLayoutNode.
                                    SlicerLayoutConventionalWidescreenView)
            self.takeScreenshot('LoadingADICOMVolume-ConventionalWidescreen',
                                'Conventional Widescreen Layout', -1)

            slicer.util.mainWindow().moduleSelector().selectModule(
                'VolumeRendering')
            self.takeScreenshot('VolumeRendering-Module', 'Volume Rendering',
                                -1)

            volumeRenderingWidgetRep = slicer.modules.volumerendering.widgetRepresentation(
            )
            abdomenVolume = slicer.mrmlScene.GetFirstNodeByName(
                '6: CT_Thorax_Abdomen')
            volumeRenderingWidgetRep.setMRMLVolumeNode(abdomenVolume)
            self.takeScreenshot('VolumeRendering-SelectVolume',
                                'Select the volume 6: CT_Thorax_Abdomen', -1)

            presetsScene = slicer.modules.volumerendering.logic(
            ).GetPresetsScene()
            ctCardiac3 = presetsScene.GetFirstNodeByName('CT-Cardiac3')
            volumeRenderingWidgetRep.applyPreset(ctCardiac3)
            self.takeScreenshot('VolumeRendering-SelectPreset',
                                'Select the Preset CT-Cardiac-3')

            self.delayDisplay('Skipping: Select VTK CPU Ray Casting')

            volumeRenderingNode = slicer.mrmlScene.GetFirstNodeByName(
                'VolumeRendering')
            volumeRenderingNode.SetVisibility(1)
            self.takeScreenshot('VolumeRendering-ViewRendering',
                                'View Volume Rendering', -1)

            self.delayDisplay('Skipping Move the Shift slider')

            redWidget.sliceController().setSliceVisible(False)
            self.takeScreenshot('VolumeRendering-SlicesOff',
                                'Turn off visibility of slices in 3D', -1)

            threeDView = layoutManager.threeDWidget(0).threeDView()
            self.clickAndDrag(threeDView)
            self.takeScreenshot('VolumeRendering-RotateVolumeRendering',
                                'Rotate volume rendered image', -1)

            volumeRenderingNode.SetVisibility(0)
            self.takeScreenshot('VolumeRendering-TurnOffVolumeRendering',
                                'Turn off volume rendered image', -1)

            volumeRenderingNode.SetCroppingEnabled(1)
            annotationROI = slicer.mrmlScene.GetFirstNodeByName(
                'AnnotationROI')
            annotationROI.SetDisplayVisibility(1)
            self.takeScreenshot('VolumeRendering-DisplayROI',
                                'Enable cropping and display ROI', -1)

            redWidget.sliceController().setSliceVisible(True)
            self.takeScreenshot('VolumeRendering-SlicesOn',
                                'Turn on visibility of slices in 3D', -1)

            annotationROI.SetXYZ(-79.61, 154.16, -232.591)
            annotationROI.SetRadiusXYZ(43.4, 65.19, 70.5)
            self.takeScreenshot('VolumeRendering-SizedROI',
                                'Position the ROI over a kidney', -1)

            volumeRenderingNode.SetVisibility(1)
            self.takeScreenshot('VolumeRendering-ROIRendering',
                                'ROI volume rendered', -1)

            annotationROI.SetXYZ(15, 146, -186)
            annotationROI.SetRadiusXYZ(138, 57, 61)
            self.takeScreenshot('VolumeRendering-BothKidneys',
                                'Rendered both kidneys', -1)

            self.delayDisplay('Test passed!')
        except Exception, e:
            import traceback
            traceback.print_exc()
            self.delayDisplay('Test caused exception!\n' + str(e))
Ejemplo n.º 6
0
 def getDebuggerAutoConnect(self):
     settings = qt.QSettings()
     if settings.contains('Developer/PythonRemoteDebugAutoConnect'):
         return settings.value(
             'Developer/PythonRemoteDebugAutoConnect').lower() == 'true'
     return False
Ejemplo n.º 7
0
 def __init__(self,files):
   self.files = files
   settings = qt.QSettings()
   self.sendAddress = settings.value('DICOM.sendAddress')
   self.sendPort = settings.value('DICOM.sendPort')
   self.open
Ejemplo n.º 8
0
 def setBrowserPersistence(self,state):
   self.browserPersistent = state
   self.setModality(not self.browserPersistent)
   settings = qt.QSettings()
   browserPersistentSettingString = 'true' if self.browserPersistent else 'false'
   settings.setValue('DICOM/BrowserPersistent', browserPersistentSettingString)
Ejemplo n.º 9
0
 def onHorizontalViewCheckBox(self):
   settings = qt.QSettings()
   self.tableSplitter.setOrientation(self.horizontalViewCheckBox.checked)
   settings.setValue('DICOM/horizontalTables', int(self.horizontalViewCheckBox.checked))
Ejemplo n.º 10
0
    def section_LoadInputData(self):
        try:
            # Download and unzip test CT DICOM data
            import urllib
            downloads = ((
                'http://slicer.kitware.com/midas3/download/item/137843/TestDicomCT.zip',
                self.dicomZipFilePath), )

            downloaded = 0
            for url, filePath in downloads:
                if not os.path.exists(filePath) or os.stat(
                        filePath).st_size == 0:
                    if downloaded == 0:
                        self.delayDisplay(
                            'Downloading input data to folder\n' +
                            self.dicomZipFilePath +
                            '.\n\n  It may take a few minutes...',
                            self.delayMs)
                    print('Requesting download from %s...' % (url))
                    urllib.urlretrieve(url, filePath)
                    downloaded += 1
                else:
                    self.delayDisplay(
                        'Input data has been found in folder ' +
                        self.dicomZipFilePath, self.delayMs)
            if downloaded > 0:
                self.delayDisplay('Downloading input data finished',
                                  self.delayMs)

            numOfFilesInDicomDataDir = len([
                name for name in os.listdir(self.dicomDataDir)
                if os.path.isfile(self.dicomDataDir + '/' + name)
            ])
            if (numOfFilesInDicomDataDir !=
                    self.expectedNumOfFilesInDicomDataDir):
                slicer.app.applicationLogic().Unzip(self.dicomZipFilePath,
                                                    self.dicomDataDir)
                self.delayDisplay("Unzipping done", self.delayMs)

            numOfFilesInDicomDataDirTest = len([
                name for name in os.listdir(self.dicomDataDir)
                if os.path.isfile(self.dicomDataDir + '/' + name)
            ])
            self.assertTrue(numOfFilesInDicomDataDirTest ==
                            self.expectedNumOfFilesInDicomDataDir)

            # Open test database and empty it
            qt.QDir().mkpath(self.dicomDatabaseDir)

            if slicer.dicomDatabase:
                self.originalDatabaseDirectory = os.path.split(
                    slicer.dicomDatabase.databaseFilename)[0]
            else:
                self.originalDatabaseDirectory = None
                settings = qt.QSettings()
                settings.setValue('DatabaseDirectory', self.dicomDatabaseDir)

            dicomWidget = slicer.modules.dicom.widgetRepresentation().self()
            dicomWidget.onDatabaseDirectoryChanged(self.dicomDatabaseDir)
            self.assertTrue(slicer.dicomDatabase.isOpen)

            # Import test data in database
            indexer = ctk.ctkDICOMIndexer()
            self.assertTrue(indexer)

            indexer.addDirectory(slicer.dicomDatabase, self.dicomDataDir)

            self.assertTrue(len(slicer.dicomDatabase.patients()) == 1)
            self.assertTrue(slicer.dicomDatabase.patients()[0])

            # Load test data
            numOfScalarVolumeNodesBeforeLoad = len(
                slicer.util.getNodes('vtkMRMLScalarVolumeNode*'))
            numOfSubjectHierarchyNodesBeforeLoad = len(
                slicer.util.getNodes('vtkMRMLSubjectHierarchyNode*'))

            patient = slicer.dicomDatabase.patients()[0]
            studies = slicer.dicomDatabase.studiesForPatient(patient)
            series = [
                slicer.dicomDatabase.seriesForStudy(study) for study in studies
            ]
            seriesUIDs = [uid for uidList in series for uid in uidList]
            dicomWidget.detailsPopup.offerLoadables(seriesUIDs,
                                                    'SeriesUIDList')
            dicomWidget.detailsPopup.loadCheckedLoadables()

            self.assertTrue(
                len(slicer.util.getNodes('vtkMRMLScalarVolumeNode*')) ==
                numOfScalarVolumeNodesBeforeLoad + 1)
            self.assertTrue(
                len(slicer.util.getNodes('vtkMRMLSubjectHierarchyNode*')) ==
                numOfSubjectHierarchyNodesBeforeLoad + 3)

        except Exception, e:
            import traceback
            traceback.print_exc()
            self.delayDisplay('Test caused exception!\n' + str(e),
                              self.delayMs * 2)
Ejemplo n.º 11
0
 def onTableDensityComboBox(self, state):
   settings = qt.QSettings()
   settings.setValue('DICOM/tableDensity', state)
 def setSetting(settingStr, value):
   settingStr = "LongitudinalPETCT/"+settingStr 
   qt.QSettings().setValue(settingStr,str(value))
Ejemplo n.º 13
0
 def __resetDatabase(self):
     """
   As stated.
   """
     self.database = qt.QSettings(self.filepath, self.dbFormat)
Ejemplo n.º 14
0
 def getSecret(self):
     settings = qt.QSettings()
     if settings.contains('Developer/PtvsdSecret'):
         secret = settings.value('Developer/PtvsdSecret')
         return secret
     return "slicer"
Ejemplo n.º 15
0
 def onRunListenerAtStart(self):
     settings = qt.QSettings()
     settings.setValue('DICOM/RunListenerAtStart',
                       self.runListenerAtStart.checked)
Ejemplo n.º 16
0
 def onPopupGeometryChanged(self):
   settings = qt.QSettings()
   self.popupGeometry = self.window.geometry
   settings.setValue('DICOM/detailsPopup.geometry', self.window.geometry)
Ejemplo n.º 17
0
    def loadModules(self, path, depth=1):
        # Get list of modules in specified path
        modules = ModuleInfo.findModules(path, depth)

        # Determine which modules in above are not already loaded
        factory = slicer.app.moduleManager().factoryManager()
        loadedModules = factory.instantiatedModuleNames()

        candidates = [m for m in modules if m.key not in loadedModules]

        # Prompt to load additional module(s)
        if len(candidates):
            dlg = LoadModulesDialog(self.parent.window())
            dlg.setModules(candidates)

            if dlg.exec_() == qt.QDialog.Accepted:
                modulesToLoad = dlg.selectedModules

                # Add module(s) to permanent search paths, if requested
                if dlg.addToSearchPaths:
                    settings = slicer.app.revisionUserSettings()
                    rawSearchPaths = list(
                        _settingsList(settings, "Modules/AdditionalPaths"))
                    searchPaths = [qt.QDir(path) for path in rawSearchPaths]
                    modified = False

                    for module in modulesToLoad:
                        rawPath = os.path.dirname(module.path)
                        path = qt.QDir(rawPath)
                        if not path in searchPaths:
                            searchPaths.append(path)
                            rawSearchPaths.append(rawPath)
                            modified = True

                    if modified:
                        settings.setValue("Modules/AdditionalPaths",
                                          rawSearchPaths)

                # Enable developer mode (shows Reload&Test section, etc.), if requested
                if dlg.enableDeveloperMode:
                    qt.QSettings().setValue('Developer/DeveloperMode', 'true')

                # Register requested module(s)
                failed = []

                for module in modulesToLoad:
                    factory.registerModule(qt.QFileInfo(module.path))
                    if not factory.isRegistered(module.key):
                        failed.append(module)

                if len(failed):
                    md = qt.QMessageBox(self.parent.window())
                    md.icon = qt.QMessageBox.Critical
                    md.standardButtons = qt.QMessageBox.Close
                    md.windowTitle = "Module loading failed"

                    if len(failed) > 1:
                        md.text = "The following modules could not be registered:"

                    else:
                        md.text = "The '%s' module could not be registered:" % failed[
                            0].key

                    failedFormat = "<ul><li>%(key)s<br/>(%(path)s)</li></ul>"
                    md.informativeText = "".join(
                        [failedFormat % m.__dict__ for m in failed])

                    md.exec_()
                    return

                # Instantiate and load requested module(s)
                if not factory.loadModules(
                    [module.key for module in modulesToLoad]):
                    md = qt.QMessageBox(self.parent.window())
                    md.icon = qt.QMessageBox.Critical
                    md.standardButtons = qt.QMessageBox.Close
                    md.windowTitle = "Error loading module(s)"
                    md.text = (
                        "The module factory manager reported an error. "
                        "One or more of the requested module(s) and/or "
                        "dependencies thereof may not have been loaded.")
                    md.exec_()
Ejemplo n.º 18
0
    def load(self, loadable):

        imageSeriesAndFiles = self.__seperateFilesListIntoImageSeries(
            loadable.files)
        studiesAndImageSeries = self.__seperateImageSeriesAndFilesIntoStudies(
            imageSeriesAndFiles)

        petImageSeriesInStudies = self.__extractSpecificModalitySeriesForStudies(
            studiesAndImageSeries, imageSeriesAndFiles, self.petTerm)
        ctImageSeriesInStudies = self.__extractSpecificModalitySeriesForStudies(
            studiesAndImageSeries, imageSeriesAndFiles, self.ctTerm)

        patientID = None
        patientName = None
        patientBirthDate = None
        patientSex = None
        patientHeight = None

        probeFiles = None
        if imageSeriesAndFiles.keys():
            probeImgSerUID = imageSeriesAndFiles.keys()[0]
            probeFiles = imageSeriesAndFiles[probeImgSerUID]

        if probeFiles:
            patientID = self.__getSeriesInformation(probeFiles,
                                                    self.tags['patientID'])
            patientName = self.__getSeriesInformation(probeFiles,
                                                      self.tags['patientName'])
            patientBirthDate = self.__getSeriesInformation(
                probeFiles, self.tags['patientBirthDate'])
            patientSex = self.__getSeriesInformation(probeFiles,
                                                     self.tags['patientSex'])
            patientHeight = self.__getSeriesInformation(
                probeFiles, self.tags['patientHeight'])

        reportNode = None

        # import into existing Report node
        matchingReports = []
        reportNodes = slicer.mrmlScene.GetNodesByClass(
            'vtkMRMLLongitudinalPETCTReportNode')
        reportNodes.SetReferenceCount(reportNodes.GetReferenceCount() - 1)

        for i in xrange(reportNodes.GetNumberOfItems()):
            rn = reportNodes.GetItemAsObject(i)
            if (rn.GetAttribute("DICOM.PatientID") == patientID) | (
                (rn.GetAttribute("DICOM.PatientName") == patientName) &
                (rn.GetAttribute("DICOM.PatientBirthDate") == patientBirthDate)
                    & (rn.GetAttribute("DICOM.PatientSex") == patientSex)):
                matchingReports.append(i)

        if matchingReports:
            selectables = []
            selectables.append("Create new PET/CT Report")
            for id in matchingReports:
                rn = reportNodes.GetItemAsObject(id)
                selectables.append("Import into " + rn.GetName() +
                                   " --- Number of available studies: " +
                                   str(rn.GetNumberOfStudyNodeIDs()))
            dialogTitle = "Import PET/CT Studies into existing Report"
            dialogLabel = "One or more Reports for the selected Patient already exist!"
            selected = qt.QInputDialog.getItem(None, dialogTitle, dialogLabel,
                                               selectables, 0, False)

            if (selected in selectables) & (selected != selectables[0]):
                reportNode = reportNodes.GetItemAsObject(
                    selectables.index(selected) - 1)

        # create new Report node
        if not reportNode:
            reportNode = slicer.mrmlScene.CreateNodeByClass(
                'vtkMRMLLongitudinalPETCTReportNode')
            reportNode.SetReferenceCount(reportNode.GetReferenceCount() - 1)
            slicer.mrmlScene.AddNode(reportNode)

            reportNode.SetName("Report for " + patientName)
            reportNode.SetAttribute('DICOM.PatientID', patientID)
            reportNode.SetAttribute('DICOM.PatientName', patientName)
            reportNode.SetAttribute('DICOM.PatientBirthDate', patientBirthDate)
            reportNode.SetAttribute('DICOM.PatientSex', patientSex)
            reportNode.SetAttribute('DICOM.PatientHeight', patientHeight)

        # setup Report's default color node and table for Finding types
        colorLogic = slicer.modules.colors.logic()
        defaultColorNodeID = colorLogic.GetDefaultEditorColorNodeID()
        colorTableNode = slicer.mrmlScene.GetNodeByID(defaultColorNodeID)
        reportNode.SetColorTableNodeID(colorTableNode.GetID())

        for studyUID in studiesAndImageSeries.keys():

            if reportNode.IsStudyInReport(studyUID):
                continue

            petDescriptions = []  #petImageSeriesInStudies[studyUID]
            ctDescriptions = []
            studyDescription = None

            for petUID in petImageSeriesInStudies[studyUID]:
                petDescriptions.append(
                    self.__getImageSeriesDescription(
                        imageSeriesAndFiles[petUID]))

                if not studyDescription:
                    date = self.__getSeriesInformation(
                        imageSeriesAndFiles[petUID], self.tags['studyDate'])
                    date = date[:4] + "." + date[4:6] + "." + date[6:8]
                    time = self.__getSeriesInformation(
                        imageSeriesAndFiles[petUID], self.tags['studyTime'])
                    time = time[:2] + ":" + time[2:4] + ":" + time[4:6]
                    studyDescription = "Multiple PET and/or CT image series have been found for the Study from " + date + " " + time + ". Please change the selection if a different pair of image series should be loaded."

            for ctUID in ctImageSeriesInStudies[studyUID]:
                ctDescriptions.append(
                    self.__getImageSeriesDescription(
                        imageSeriesAndFiles[ctUID]))

            selectedIndexes = [0, 0]

            if (len(petDescriptions) > 1) | (len(ctDescriptions) > 1):
                dialog = PETCTSeriesSelectorDialog(None, studyDescription,
                                                   petDescriptions,
                                                   ctDescriptions, 0, 0)
                dialog.parent.exec_()
                selectedIndexes = dialog.getSelectedSeries()

            # UID of current PET series
            petImageSeriesUID = petImageSeriesInStudies[studyUID][
                selectedIndexes[0]]
            # UID of current CT series
            ctImageSeriesUID = None
            if ctDescriptions:  # for PET only support
                ctImageSeriesUID = ctImageSeriesInStudies[studyUID][
                    selectedIndexes[1]]

            # create PET SUV volume node
            petVolumeNode = self.scalarVolumePlugin.load(
                self.getCachedLoadables(
                    imageSeriesAndFiles[petImageSeriesUID])[0])
            petDir = self.__getDirectoryOfImageSeries(
                self.__getSeriesInformation(
                    imageSeriesAndFiles[petImageSeriesUID],
                    self.tags['sopInstanceUID']))

            parameters = {}
            parameters["PETVolume"] = petVolumeNode.GetID()
            parameters["PETDICOMPath"] = petDir
            parameters["SUVVolume"] = petVolumeNode.GetID()

            quantificationCLI = qt.QSettings().value(
                'LongitudinalPETCT/quantificationCLIModule')

            if quantificationCLI == None:
                quantificationCLI = "petsuvimagemaker"

            cliNode = None
            cliNode = slicer.cli.run(
                getattr(slicer.modules, quantificationCLI), cliNode,
                parameters)

            # create PET label volume node
            volLogic = slicer.modules.volumes.logic()
            petLabelVolumeNode = volLogic.CreateAndAddLabelVolume(
                slicer.mrmlScene, petVolumeNode,
                petVolumeNode.GetName() + "_LabelVolume")

            ctVolumeNode = None
            # create CT volume node
            if ctImageSeriesUID:
                ctVolumeNode = self.scalarVolumePlugin.load(
                    self.getCachedLoadables(
                        imageSeriesAndFiles[ctImageSeriesUID])[0])

            # create Study node
            studyNode = slicer.mrmlScene.CreateNodeByClass(
                'vtkMRMLLongitudinalPETCTStudyNode')
            studyNode.SetReferenceCount(studyNode.GetReferenceCount() - 1)

            studyNode.SetName("Study_" + self.__getSeriesInformation(
                imageSeriesAndFiles[petImageSeriesUID], self.tags['studyDate'])
                              )

            studyNode.SetAttribute(
                'DICOM.StudyID',
                self.__getSeriesInformation(
                    imageSeriesAndFiles[petImageSeriesUID],
                    self.tags['studyID']))
            studyNode.SetAttribute(
                'DICOM.StudyInstanceUID',
                self.__getSeriesInformation(
                    imageSeriesAndFiles[petImageSeriesUID],
                    self.tags['studyInstanceUID']))
            studyNode.SetAttribute(
                'DICOM.StudyDate',
                self.__getSeriesInformation(
                    imageSeriesAndFiles[petImageSeriesUID],
                    self.tags['studyDate']))
            studyNode.SetAttribute(
                'DICOM.StudyTime',
                self.__getSeriesInformation(
                    imageSeriesAndFiles[petImageSeriesUID],
                    self.tags['studyTime']))
            studyNode.SetAttribute(
                'DICOM.RadioPharmaconStartTime',
                self.__getSeriesInformation(
                    imageSeriesAndFiles[petImageSeriesUID],
                    self.tags['radioPharmaconStartTime']))
            studyNode.SetAttribute(
                'DICOM.DecayFactor',
                self.__getSeriesInformation(
                    imageSeriesAndFiles[petImageSeriesUID],
                    self.tags['decayCorrection']))
            studyNode.SetAttribute(
                'DICOM.DecayCorrection',
                self.__getSeriesInformation(
                    imageSeriesAndFiles[petImageSeriesUID],
                    self.tags['decayFactor']))
            studyNode.SetAttribute(
                'DICOM.FrameReferenceTime',
                self.__getSeriesInformation(
                    imageSeriesAndFiles[petImageSeriesUID],
                    self.tags['frameRefTime']))
            studyNode.SetAttribute(
                'DICOM.RadionuclideHalfLife',
                self.__getSeriesInformation(
                    imageSeriesAndFiles[petImageSeriesUID],
                    self.tags['radionuclideHalfLife']))
            studyNode.SetAttribute(
                'DICOM.PETSeriesTime',
                self.__getSeriesInformation(
                    imageSeriesAndFiles[petImageSeriesUID],
                    self.tags['seriesTime']))
            if ctImageSeriesUID:
                studyNode.SetAttribute(
                    'DICOM.CTSeriesTime',
                    self.__getSeriesInformation(
                        imageSeriesAndFiles[ctImageSeriesUID],
                        self.tags['seriesTime']))
            studyNode.SetAttribute(
                'DICOM.PatientWeight',
                self.__getSeriesInformation(
                    imageSeriesAndFiles[petImageSeriesUID],
                    self.tags['patientWeight']))

            studyNode.SetAndObservePETVolumeNodeID(petVolumeNode.GetID())
            if ctVolumeNode:
                studyNode.SetAndObserveCTVolumeNodeID(ctVolumeNode.GetID())
            studyNode.SetAndObservePETLabelVolumeNodeID(
                petLabelVolumeNode.GetID())

            slicer.mrmlScene.AddNode(studyNode)
            reportNode.AddStudyNodeID(studyNode.GetID())

        return reportNode