|  | @@ -1,318 +0,0 @@
 | 
	
		
			
				|  |  | -import os
 | 
	
		
			
				|  |  | -import unittest
 | 
	
		
			
				|  |  | -import vtk, qt, ctk, slicer
 | 
	
		
			
				|  |  | -from slicer.ScriptedLoadableModule import *
 | 
	
		
			
				|  |  | -import logging
 | 
	
		
			
				|  |  | -import slicerNetwork
 | 
	
		
			
				|  |  | -import json
 | 
	
		
			
				|  |  | -import loadPatient
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -#
 | 
	
		
			
				|  |  | -# dataExplorer
 | 
	
		
			
				|  |  | -#
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -class dataExplorer(ScriptedLoadableModule):
 | 
	
		
			
				|  |  | -  """Uses ScriptedLoadableModule base class, available at:
 | 
	
		
			
				|  |  | -  https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py
 | 
	
		
			
				|  |  | -  """
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  def __init__(self, parent):
 | 
	
		
			
				|  |  | -    ScriptedLoadableModule.__init__(self, parent)
 | 
	
		
			
				|  |  | -    self.parent.title = "dataExplorer" # TODO make this more human readable by adding spaces
 | 
	
		
			
				|  |  | -    self.parent.categories = ["EMBRACE"]
 | 
	
		
			
				|  |  | -    self.parent.dependencies = []
 | 
	
		
			
				|  |  | -    self.parent.contributors = ["Andrej Studen (University of Ljubljana)"] # replace with "Firstname Lastname (Organization)"
 | 
	
		
			
				|  |  | -    self.parent.helpText = """
 | 
	
		
			
				|  |  | -This is an example of scripted loadable module bundled in an extension.
 | 
	
		
			
				|  |  | -It performs a simple thresholding on the input volume and optionally captures a screenshot.
 | 
	
		
			
				|  |  | -"""
 | 
	
		
			
				|  |  | -    self.parent.helpText += self.getDefaultModuleDocumentationLink()
 | 
	
		
			
				|  |  | -    self.parent.acknowledgementText = """
 | 
	
		
			
				|  |  | -This extension developed within Medical Physics research programe of ARRS
 | 
	
		
			
				|  |  | -""" # replace with organization, grant and thanks.
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -#
 | 
	
		
			
				|  |  | -# dataExplorerWidget
 | 
	
		
			
				|  |  | -#
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -class dataExplorerWidget(ScriptedLoadableModuleWidget):
 | 
	
		
			
				|  |  | -  """Uses ScriptedLoadableModuleWidget base class, available at:
 | 
	
		
			
				|  |  | -  https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py
 | 
	
		
			
				|  |  | -  """
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  def setup(self):
 | 
	
		
			
				|  |  | -    ScriptedLoadableModuleWidget.setup(self)
 | 
	
		
			
				|  |  | -    self.loadPatientLogic=loadPatient.loadPatientLogic(self)
 | 
	
		
			
				|  |  | -    # Instantiate and connect widgets ...
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    #
 | 
	
		
			
				|  |  | -    # Parameters Area
 | 
	
		
			
				|  |  | -    #
 | 
	
		
			
				|  |  | -    basePath=os.path.join(os.path.expanduser('~'),'.EMBRACE')
 | 
	
		
			
				|  |  | -    #'D:\\Sw\\src\\EMBRACE'
 | 
	
		
			
				|  |  | -    netConfig=os.path.join(basePath,'onko-nix.json')
 | 
	
		
			
				|  |  | -    self.sNet=slicerNetwork.labkeyURIHandler()
 | 
	
		
			
				|  |  | -    self.sNet.parseConfig(netConfig)
 | 
	
		
			
				|  |  | -    self.sNet.initRemote()
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    self.loadPatientLogic.setURIHandler(self.sNet)
 | 
	
		
			
				|  |  | -    self.project="EMBRACE/Studija"
 | 
	
		
			
				|  |  | -    self.dataset="ImagingVisitsManaged"
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    datasetCollapsibleButton = ctk.ctkCollapsibleButton()
 | 
	
		
			
				|  |  | -    datasetCollapsibleButton.text = "Dataset"
 | 
	
		
			
				|  |  | -    self.layout.addWidget(datasetCollapsibleButton)
 | 
	
		
			
				|  |  | -    # Layout within the dummy collapsible button
 | 
	
		
			
				|  |  | -    datasetFormLayout = qt.QFormLayout(datasetCollapsibleButton)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    self.datasetButton=qt.QPushButton("Load")
 | 
	
		
			
				|  |  | -    self.datasetButton.connect('clicked(bool)',self.onDatasetLoadButtonClicked)
 | 
	
		
			
				|  |  | -    datasetFormLayout.addRow("Data:",self.datasetButton)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    self.patientId=qt.QLineEdit("LJU004")
 | 
	
		
			
				|  |  | -    datasetFormLayout.addRow("Patient ID:",self.patientId)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    loadCTButton=qt.QPushButton("CT")
 | 
	
		
			
				|  |  | -    loadCTButton.clicked.connect(self.onLoadCTButtonClicked)
 | 
	
		
			
				|  |  | -    datasetFormLayout.addRow("Load:",loadCTButton)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    loadCTRSButton=qt.QPushButton("CT-RS")
 | 
	
		
			
				|  |  | -    loadCTRSButton.clicked.connect(self.onLoadCTRSButtonClicked)
 | 
	
		
			
				|  |  | -    datasetFormLayout.addRow("Load:",loadCTRSButton)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    loadDMRButton=qt.QPushButton("DMR")
 | 
	
		
			
				|  |  | -    loadDMRButton.clicked.connect(self.onLoadDMRButtonClicked)
 | 
	
		
			
				|  |  | -    datasetFormLayout.addRow("Load:",loadDMRButton)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    dataCollapsibleButton = ctk.ctkCollapsibleButton()
 | 
	
		
			
				|  |  | -    dataCollapsibleButton.text = "Data"
 | 
	
		
			
				|  |  | -    self.layout.addWidget(dataCollapsibleButton)
 | 
	
		
			
				|  |  | -    # Layout within the dummy collapsible button
 | 
	
		
			
				|  |  | -    dataFormLayout = qt.QVBoxLayout(dataCollapsibleButton)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    self.data=qt.QTableWidget(3,3)
 | 
	
		
			
				|  |  | -    dataFormLayout.addWidget(self.data)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    patientsCollapsibleButton = ctk.ctkCollapsibleButton()
 | 
	
		
			
				|  |  | -    patientsCollapsibleButton.text = "Patients"
 | 
	
		
			
				|  |  | -    self.layout.addWidget(patientsCollapsibleButton)
 | 
	
		
			
				|  |  | -    # Layout within the dummy collapsible button
 | 
	
		
			
				|  |  | -    self.patientsFormLayout = qt.QVBoxLayout(patientsCollapsibleButton)
 | 
	
		
			
				|  |  | -    self.signalMapper=qt.QSignalMapper()
 | 
	
		
			
				|  |  | -    self.signalMapper.connect(self.signalMapper, qt.SIGNAL("mapped(const QString &)"), self.onPatientButtonClicked)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  def cleanup(self):
 | 
	
		
			
				|  |  | -    pass
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  def onDatasetLoadButtonClicked(self):
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -      ds=self.sNet.loadDataset(self.project,self.dataset)
 | 
	
		
			
				|  |  | -      #loaded a JSON object -> convert to suitable display
 | 
	
		
			
				|  |  | -      columns=ds['columnModel']
 | 
	
		
			
				|  |  | -      self.data.setColumnCount(len(columns))
 | 
	
		
			
				|  |  | -      self.data.setRowCount(len(ds['rows']))
 | 
	
		
			
				|  |  | -      columnNames=[f['header'] for f in columns]
 | 
	
		
			
				|  |  | -      self.data.setHorizontalHeaderLabels(columnNames)
 | 
	
		
			
				|  |  | -      columnID=[f['dataIndex'] for f in columns]
 | 
	
		
			
				|  |  | -      insertRowID=0
 | 
	
		
			
				|  |  | -      for row in ds['rows']:
 | 
	
		
			
				|  |  | -          insertColumnID=0
 | 
	
		
			
				|  |  | -          for c in columnID:
 | 
	
		
			
				|  |  | -              item=qt.QTableWidgetItem(str(row[c]))
 | 
	
		
			
				|  |  | -              self.data.setItem(insertRowID,insertColumnID,item)
 | 
	
		
			
				|  |  | -              insertColumnID+=1
 | 
	
		
			
				|  |  | -          insertRowID+=1
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -      #clear patient list
 | 
	
		
			
				|  |  | -      while self.patientsFormLayout.count():
 | 
	
		
			
				|  |  | -           child=self.patientsFormLayout.takeAt(0)
 | 
	
		
			
				|  |  | -           if child.widget():
 | 
	
		
			
				|  |  | -               self.signalMapper.removeMapping(child.widget())
 | 
	
		
			
				|  |  | -               child.widget().deleteLater()
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -      #populate patient list
 | 
	
		
			
				|  |  | -      patientList=[f['EMBRACE_ID'] for f in ds['rows']]
 | 
	
		
			
				|  |  | -      #remove duplicates
 | 
	
		
			
				|  |  | -      patientSet=set(patientList)
 | 
	
		
			
				|  |  | -      for p in patientSet:
 | 
	
		
			
				|  |  | -           patientButton=qt.QPushButton(p)
 | 
	
		
			
				|  |  | -           patientButton.connect(patientButton, qt.SIGNAL("clicked()"),
 | 
	
		
			
				|  |  | -                self.signalMapper, qt.SLOT("map()"))
 | 
	
		
			
				|  |  | -           self.patientsFormLayout.addWidget(patientButton)
 | 
	
		
			
				|  |  | -           self.signalMapper.setMapping(patientButton,p)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  def onPatientButtonClicked(self, label):
 | 
	
		
			
				|  |  | -      print "onPatientButtonClicked() for {}".format(label)
 | 
	
		
			
				|  |  | -      self.loadPatientLogic.load(label)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  def onLoadCTButtonClicked(self):
 | 
	
		
			
				|  |  | -      self.CT=self.loadPatientLogic.loadCT(self.patientId.text)
 | 
	
		
			
				|  |  | -      self.CT[0]['node'].SetName(self.patientId.text+"_CT")
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  def onLoadCTRSButtonClicked(self):
 | 
	
		
			
				|  |  | -      self.CTRS=self.loadPatientLogic.loadCTRS(self.patientId.text)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  def onLoadDMRButtonClicked(self):
 | 
	
		
			
				|  |  | -      self.DMR=self.loadPatientLogic.loadDMR(self.patientId.text)
 | 
	
		
			
				|  |  | -      self.DMR[0]['node'].SetName(self.patientId.text+"_DMR")
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -#
 | 
	
		
			
				|  |  | -# dataExplorerLogic
 | 
	
		
			
				|  |  | -#
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -class dataExplorerLogic(ScriptedLoadableModuleLogic):
 | 
	
		
			
				|  |  | -  """This class should implement all the actual
 | 
	
		
			
				|  |  | -  computation done by your module.  The interface
 | 
	
		
			
				|  |  | -  should be such that other python code can import
 | 
	
		
			
				|  |  | -  this class and make use of the functionality without
 | 
	
		
			
				|  |  | -  requiring an instance of the Widget.
 | 
	
		
			
				|  |  | -  Uses ScriptedLoadableModuleLogic base class, available at:
 | 
	
		
			
				|  |  | -  https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py
 | 
	
		
			
				|  |  | -  """
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  def hasImageData(self,volumeNode):
 | 
	
		
			
				|  |  | -    """This is an example logic method that
 | 
	
		
			
				|  |  | -    returns true if the passed in volume
 | 
	
		
			
				|  |  | -    node has valid image data
 | 
	
		
			
				|  |  | -    """
 | 
	
		
			
				|  |  | -    if not volumeNode:
 | 
	
		
			
				|  |  | -      logging.debug('hasImageData failed: no volume node')
 | 
	
		
			
				|  |  | -      return False
 | 
	
		
			
				|  |  | -    if volumeNode.GetImageData() is None:
 | 
	
		
			
				|  |  | -      logging.debug('hasImageData failed: no image data in volume node')
 | 
	
		
			
				|  |  | -      return False
 | 
	
		
			
				|  |  | -    return True
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  def isValidInputOutputData(self, inputVolumeNode, outputVolumeNode):
 | 
	
		
			
				|  |  | -    """Validates if the output is not the same as input
 | 
	
		
			
				|  |  | -    """
 | 
	
		
			
				|  |  | -    if not inputVolumeNode:
 | 
	
		
			
				|  |  | -      logging.debug('isValidInputOutputData failed: no input volume node defined')
 | 
	
		
			
				|  |  | -      return False
 | 
	
		
			
				|  |  | -    if not outputVolumeNode:
 | 
	
		
			
				|  |  | -      logging.debug('isValidInputOutputData failed: no output volume node defined')
 | 
	
		
			
				|  |  | -      return False
 | 
	
		
			
				|  |  | -    if inputVolumeNode.GetID()==outputVolumeNode.GetID():
 | 
	
		
			
				|  |  | -      logging.debug('isValidInputOutputData failed: input and output volume is the same. Create a new volume for output to avoid this error.')
 | 
	
		
			
				|  |  | -      return False
 | 
	
		
			
				|  |  | -    return True
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  def takeScreenshot(self,name,description,type=-1):
 | 
	
		
			
				|  |  | -    # show the message even if not taking a screen shot
 | 
	
		
			
				|  |  | -    slicer.util.delayDisplay('Take screenshot: '+description+'.\nResult is available in the Annotations module.', 3000)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    lm = slicer.app.layoutManager()
 | 
	
		
			
				|  |  | -    # switch on the type to get the requested window
 | 
	
		
			
				|  |  | -    widget = 0
 | 
	
		
			
				|  |  | -    if type == slicer.qMRMLScreenShotDialog.FullLayout:
 | 
	
		
			
				|  |  | -      # full layout
 | 
	
		
			
				|  |  | -      widget = lm.viewport()
 | 
	
		
			
				|  |  | -    elif type == slicer.qMRMLScreenShotDialog.ThreeD:
 | 
	
		
			
				|  |  | -      # just the 3D window
 | 
	
		
			
				|  |  | -      widget = lm.threeDWidget(0).threeDView()
 | 
	
		
			
				|  |  | -    elif type == slicer.qMRMLScreenShotDialog.Red:
 | 
	
		
			
				|  |  | -      # red slice window
 | 
	
		
			
				|  |  | -      widget = lm.sliceWidget("Red")
 | 
	
		
			
				|  |  | -    elif type == slicer.qMRMLScreenShotDialog.Yellow:
 | 
	
		
			
				|  |  | -      # yellow slice window
 | 
	
		
			
				|  |  | -      widget = lm.sliceWidget("Yellow")
 | 
	
		
			
				|  |  | -    elif type == slicer.qMRMLScreenShotDialog.Green:
 | 
	
		
			
				|  |  | -      # green slice window
 | 
	
		
			
				|  |  | -      widget = lm.sliceWidget("Green")
 | 
	
		
			
				|  |  | -    else:
 | 
	
		
			
				|  |  | -      # default to using the full window
 | 
	
		
			
				|  |  | -      widget = slicer.util.mainWindow()
 | 
	
		
			
				|  |  | -      # reset the type so that the node is set correctly
 | 
	
		
			
				|  |  | -      type = slicer.qMRMLScreenShotDialog.FullLayout
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    # grab and convert to vtk image data
 | 
	
		
			
				|  |  | -    qimage = ctk.ctkWidgetsUtils.grabWidget(widget)
 | 
	
		
			
				|  |  | -    imageData = vtk.vtkImageData()
 | 
	
		
			
				|  |  | -    slicer.qMRMLUtils().qImageToVtkImageData(qimage,imageData)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    annotationLogic = slicer.modules.annotations.logic()
 | 
	
		
			
				|  |  | -    annotationLogic.CreateSnapShot(name, description, type, 1, imageData)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  def run(self, inputVolume, outputVolume, imageThreshold, enableScreenshots=0):
 | 
	
		
			
				|  |  | -    """
 | 
	
		
			
				|  |  | -    Run the actual algorithm
 | 
	
		
			
				|  |  | -    """
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    if not self.isValidInputOutputData(inputVolume, outputVolume):
 | 
	
		
			
				|  |  | -      slicer.util.errorDisplay('Input volume is the same as output volume. Choose a different output volume.')
 | 
	
		
			
				|  |  | -      return False
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    logging.info('Processing started')
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    # Compute the thresholded output volume using the Threshold Scalar Volume CLI module
 | 
	
		
			
				|  |  | -    cliParams = {'InputVolume': inputVolume.GetID(), 'OutputVolume': outputVolume.GetID(), 'ThresholdValue' : imageThreshold, 'ThresholdType' : 'Above'}
 | 
	
		
			
				|  |  | -    cliNode = slicer.cli.run(slicer.modules.thresholdscalarvolume, None, cliParams, wait_for_completion=True)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    # Capture screenshot
 | 
	
		
			
				|  |  | -    if enableScreenshots:
 | 
	
		
			
				|  |  | -      self.takeScreenshot('dataExplorerTest-Start','MyScreenshot',-1)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    logging.info('Processing completed')
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    return True
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -class dataExplorerTest(ScriptedLoadableModuleTest):
 | 
	
		
			
				|  |  | -  """
 | 
	
		
			
				|  |  | -  This is the test case for your scripted module.
 | 
	
		
			
				|  |  | -  Uses ScriptedLoadableModuleTest base class, available at:
 | 
	
		
			
				|  |  | -  https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py
 | 
	
		
			
				|  |  | -  """
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  def setUp(self):
 | 
	
		
			
				|  |  | -    """ Do whatever is needed to reset the state - typically a scene clear will be enough.
 | 
	
		
			
				|  |  | -    """
 | 
	
		
			
				|  |  | -    slicer.mrmlScene.Clear(0)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  def runTest(self):
 | 
	
		
			
				|  |  | -    """Run as few or as many tests as needed here.
 | 
	
		
			
				|  |  | -    """
 | 
	
		
			
				|  |  | -    self.setUp()
 | 
	
		
			
				|  |  | -    self.test_dataExplorer1()
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  def test_dataExplorer1(self):
 | 
	
		
			
				|  |  | -    """ Ideally you should have several levels of tests.  At the lowest level
 | 
	
		
			
				|  |  | -    tests should exercise the functionality of the logic with different inputs
 | 
	
		
			
				|  |  | -    (both valid and invalid).  At higher levels your tests should emulate the
 | 
	
		
			
				|  |  | -    way the user would interact with your code and confirm that it still works
 | 
	
		
			
				|  |  | -    the way you intended.
 | 
	
		
			
				|  |  | -    One of the most important features of the tests is that it should alert other
 | 
	
		
			
				|  |  | -    developers when their changes will have an impact on the behavior of your
 | 
	
		
			
				|  |  | -    module.  For example, if a developer removes a feature that you depend on,
 | 
	
		
			
				|  |  | -    your test should break so they know that the feature is needed.
 | 
	
		
			
				|  |  | -    """
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    self.delayDisplay("Starting the test")
 | 
	
		
			
				|  |  | -    #
 | 
	
		
			
				|  |  | -    # first, get some data
 | 
	
		
			
				|  |  | -    #
 | 
	
		
			
				|  |  | -    import urllib
 | 
	
		
			
				|  |  | -    downloads = (
 | 
	
		
			
				|  |  | -        ('http://slicer.kitware.com/midas3/download?items=5767', 'FA.nrrd', slicer.util.loadVolume),
 | 
	
		
			
				|  |  | -        )
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    for url,name,loader in downloads:
 | 
	
		
			
				|  |  | -      filePath = slicer.app.temporaryPath + '/' + name
 | 
	
		
			
				|  |  | -      if not os.path.exists(filePath) or os.stat(filePath).st_size == 0:
 | 
	
		
			
				|  |  | -        logging.info('Requesting download %s from %s...\n' % (name, url))
 | 
	
		
			
				|  |  | -        urllib.urlretrieve(url, filePath)
 | 
	
		
			
				|  |  | -      if loader:
 | 
	
		
			
				|  |  | -        logging.info('Loading %s...' % (name,))
 | 
	
		
			
				|  |  | -        loader(filePath)
 | 
	
		
			
				|  |  | -    self.delayDisplay('Finished with download and loading')
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    volumeNode = slicer.util.getNode(pattern="FA")
 | 
	
		
			
				|  |  | -    logic = dataExplorerLogic()
 | 
	
		
			
				|  |  | -    self.assertIsNotNone( logic.hasImageData(volumeNode) )
 | 
	
		
			
				|  |  | -    self.delayDisplay('Test passed!')
 |