123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410 |
- import os
- import unittest
- from __main__ import vtk, qt, ctk, slicer
- from slicer.ScriptedLoadableModule import *
- #
- # labkeySlicerPythonExtension
- #
- class labkeySlicerPythonExtension(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 = "labkeySlicerPythonExtension" # TODO make this more human readable by adding spaces
- self.parent.categories = ["Examples"]
- self.parent.dependencies = []
- self.parent.contributors = ["Andrej Studen (UL/FMF)"] # replace with "Firstname Lastname (Organization)"
- self.parent.helpText = """
- Labkey interface to slicer
- """
- self.parent.acknowledgementText = """
- Developed within the medical physics research programme of the Slovenian research agency.
- """ # replace with organization, grant and thanks.
- #
- # labkeySlicerPythonExtensionWidget
- #
- class labkeySlicerPythonExtensionWidget(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)
- # Instantiate and connect widgets ...
- self.logic=labkeySlicerPythonExtensionLogic(self)
- #
- # Parameters Area
- #
- connectionCollapsibleButton = ctk.ctkCollapsibleButton()
- connectionCollapsibleButton.text = "Connection"
- self.layout.addWidget(connectionCollapsibleButton)
- connectionFormLayout = qt.QFormLayout(connectionCollapsibleButton)
- self.serverURL=qt.QLineEdit("https://merlin.fmf.uni-lj.si")
- connectionFormLayout.addRow("Server: ", self.serverURL)
- self.userCertButton=qt.QPushButton("Load")
- self.userCertButton.toolTip="Load user certificate (crt)"
- self.userCertButton.connect('clicked(bool)',self.onUserCertButtonClicked)
- connectionFormLayout.addRow("User certificate:",self.userCertButton)
- self.privateKeyButton=qt.QPushButton("Load")
- self.privateKeyButton.toolTip="Load private key"
- self.privateKeyButton.connect('clicked(bool)',self.onPrivateKeyButtonClicked)
- connectionFormLayout.addRow("Private key:",self.privateKeyButton)
- self.caCertButton=qt.QPushButton("Load")
- self.caCertButton.toolTip="Load CA certificate (crt)"
- self.caCertButton.connect('clicked(bool)',self.onCaCertButtonClicked)
- connectionFormLayout.addRow("CA certificate:",self.caCertButton)
- self.connectButton=qt.QPushButton("Connect")
- self.connectButton.toolTip="Connect to the server"
- self.connectButton.connect('clicked(bool)',self.onConnectButtonClicked)
- connectionFormLayout.addRow("Connection:",self.connectButton)
- parametersCollapsibleButton = ctk.ctkCollapsibleButton()
- parametersCollapsibleButton.text = "Parameters"
- self.layout.addWidget(parametersCollapsibleButton)
- # Layout within the dummy collapsible button
- parametersFormLayout = qt.QFormLayout(parametersCollapsibleButton)
- #
- # input volume selector
- #
- self.inputSelector = slicer.qMRMLNodeComboBox()
- self.inputSelector.nodeTypes = ( ("vtkMRMLScalarVolumeNode"), "" )
- self.inputSelector.addAttribute( "vtkMRMLScalarVolumeNode", "LabelMap", 0 )
- self.inputSelector.selectNodeUponCreation = True
- self.inputSelector.addEnabled = False
- self.inputSelector.removeEnabled = False
- self.inputSelector.noneEnabled = False
- self.inputSelector.showHidden = False
- self.inputSelector.showChildNodeTypes = False
- self.inputSelector.setMRMLScene( slicer.mrmlScene )
- self.inputSelector.setToolTip( "Pick the input to the algorithm." )
- parametersFormLayout.addRow("Input Volume: ", self.inputSelector)
- #
- # output volume selector
- #
- self.outputSelector = slicer.qMRMLNodeComboBox()
- self.outputSelector.nodeTypes = ( ("vtkMRMLScalarVolumeNode"), "" )
- self.outputSelector.addAttribute( "vtkMRMLScalarVolumeNode", "LabelMap", 0 )
- self.outputSelector.selectNodeUponCreation = False
- self.outputSelector.addEnabled = True
- self.outputSelector.removeEnabled = True
- self.outputSelector.noneEnabled = False
- self.outputSelector.showHidden = False
- self.outputSelector.showChildNodeTypes = False
- self.outputSelector.setMRMLScene( slicer.mrmlScene )
- self.outputSelector.setToolTip( "Pick the output to the algorithm." )
- parametersFormLayout.addRow("Output Volume: ", self.outputSelector)
- #
- # check box to trigger taking screen shots for later use in tutorials
- #
- self.enableScreenshotsFlagCheckBox = qt.QCheckBox()
- self.enableScreenshotsFlagCheckBox.checked = 0
- self.enableScreenshotsFlagCheckBox.setToolTip("If checked, take screen shots for tutorials. Use Save Data to write them to disk.")
- parametersFormLayout.addRow("Enable Screenshots", self.enableScreenshotsFlagCheckBox)
- #
- # scale factor for screen shots
- #
- self.screenshotScaleFactorSliderWidget = ctk.ctkSliderWidget()
- self.screenshotScaleFactorSliderWidget.singleStep = 1.0
- self.screenshotScaleFactorSliderWidget.minimum = 1.0
- self.screenshotScaleFactorSliderWidget.maximum = 50.0
- self.screenshotScaleFactorSliderWidget.value = 1.0
- self.screenshotScaleFactorSliderWidget.setToolTip("Set scale factor for the screen shots.")
- parametersFormLayout.addRow("Screenshot scale factor", self.screenshotScaleFactorSliderWidget)
- #
- # Apply Button
- #
- self.applyButton = qt.QPushButton("Apply")
- self.applyButton.toolTip = "Run the algorithm."
- self.applyButton.enabled = False
- parametersFormLayout.addRow(self.applyButton)
- # connections
- self.applyButton.connect('clicked(bool)', self.onApplyButton)
- self.inputSelector.connect("currentNodeChanged(vtkMRMLNode*)", self.onSelect)
- self.outputSelector.connect("currentNodeChanged(vtkMRMLNode*)", self.onSelect)
- # Add vertical spacer
- self.layout.addStretch(1)
- def cleanup(self):
- pass
- def onSelect(self):
- self.applyButton.enabled = self.inputSelector.currentNode() and self.outputSelector.currentNode()
- def onApplyButton(self):
- #logic = labkeySlicerPythonExtensionLogic()
- #enableScreenshotsFlag = self.enableScreenshotsFlagCheckBox.checked
- #screenshotScaleFactor = int(self.screenshotScaleFactorSliderWidget.value)
- print("Run the algorithm")
- #logic.run(self.inputSelector.currentNode(), self.outputSelector.currentNode(), enableScreenshotsFlag,screenshotScaleFactor)
- def onUserCertButtonClicked(self):
- startDir=os.environ['HOME']+"/temp"
- filename=qt.QFileDialog.getOpenFileName(None,'Open user certificate',
- startDir, '*.crt')
- #pwd=qt.QInputDialog.getText(None,'Certificate password',
- # 'Enter certificate password',qt.QLineEdit.Password)
- if not(filename) :
- print "No file selected"
- return
- f=qt.QFile(filename)
- if not (f.open(qt.QIODevice.ReadOnly)) :
- print "Could not open file"
- return
- certList=qt.QSslCertificate.fromPath(filename)
- if len(certList) < 1:
- print "Troubles parsing {0}".format(filename)
- return
- self.logic.cert=qt.QSslCertificate(f)
- print "cert.isNull()={0}".format(self.logic.cert.isNull())
- self.userCertButton.setText(filename)
- def onPrivateKeyButtonClicked(self):
- startDir=os.environ['HOME']+"/temp"
- filename=qt.QFileDialog.getOpenFileName(None,'Open private key',
- startDir, '*.key')
- if not (filename) :
- print "No file selected"
- return
- f=qt.QFile(filename)
- if not (f.open(qt.QIODevice.ReadOnly)) :
- print "Could not open file"
- return
- pwd=qt.QInputDialog.getText(None,'Private key password',
- 'Enter key password',qt.QLineEdit.Password)
- self.logic.key=qt.QSslKey(f,qt.QSsl.Rsa,qt.QSsl.Pem,qt.QSsl.PrivateKey,str(pwd))
- self.privateKeyButton.setText(filename)
- def onCaCertButtonClicked(self):
- startDir=os.environ['HOME']+"/temp"
- filename=qt.QFileDialog.getOpenFileName(None,'Open authority certificate',
- startDir, '*.crt')
- if not(filename) :
- print "No file selected"
- return
- f=qt.QFile(filename)
- if not (f.open(qt.QIODevice.ReadOnly)) :
- print "Could not open file"
- return
- certList=qt.QSslCertificate.fromPath(filename)
- if len(certList) < 1:
- print "Troubles parsing {0}".format(filename)
- return
- self.logic.caCert=qt.QSslCertificate(f)#certList[0]
- self.caCertButton.setText(filename)
- def onConnectButtonClicked(self):
- uname=str(self.logic.cert.subjectInfo("emailAddress"))
- uname=qt.QInputDialog.getText(None,
- "Labkey credentials","Enter username",qt.QLineEdit.Normal,uname)
- pwd=qt.QInputDialog.getText(None,
- "Labkey credentials","Enter password",qt.QLineEdit.Password)
- self.logic.connectRemote(str(self.serverURL.text),uname,pwd)
- #
- # labkeySlicerPythonExtensionLogic
- #
- class labkeySlicerPythonExtensionLogic(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 __init__(self,parent):
- ScriptedLoadableModuleLogic.__init__(self, parent)
- self.qnam=qt.QNetworkAccessManager()
- def connectRemote(self,serverURL,uname,pwd):
- request=qt.QNetworkRequest()
- request.setUrl(qt.QUrl(serverURL));
- request.setHeader(qt.QNetworkRequest.ContentTypeHeader,
- "application/x-www-form-urlencoded")
- data="email="+uname+"&password="+pwd;
- #setup the transfer
- sConfig=qt.QSslConfiguration()
- #user certificate
- sConfig.setLocalCertificate(self.cert)
- sConfig.setPrivateKey(self.key)
- #ca certificate
- caList=[self.caCert]
- sConfig.setCaCertificates(caList)
- request.setSslConfiguration(sConfig)
- #post
- r=self.qnam.post(request,data)
- connect(qnam, qt.QNetworkAccessManager.finished, self, replyFinished);
- def replyFinished(self,res):
- print "Reply finished"
- def hasImageData(self,volumeNode):
- """This is a dummy logic method that
- returns true if the passed in volume
- node has valid image data
- """
- if not volumeNode:
- print('no volume node')
- return False
- if volumeNode.GetImageData() == None:
- print('no image data')
- return False
- return True
- def takeScreenshot(self,name,description,type=-1):
- # show the message even if not taking a screen shot
- self.delayDisplay(description)
- if self.enableScreenshots == 0:
- return
- 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
- qpixMap = qt.QPixmap().grabWidget(widget)
- qimage = qpixMap.toImage()
- imageData = vtk.vtkImageData()
- slicer.qMRMLUtils().qImageToVtkImageData(qimage,imageData)
- annotationLogic = slicer.modules.annotations.logic()
- annotationLogic.CreateSnapShot(name, description, type, self.screenshotScaleFactor, imageData)
- def run(self,inputVolume,outputVolume,enableScreenshots=0,screenshotScaleFactor=1):
- """
- Run the actual algorithm
- """
- self.delayDisplay('Running the aglorithm')
- self.enableScreenshots = enableScreenshots
- self.screenshotScaleFactor = screenshotScaleFactor
- self.takeScreenshot('labkeySlicerPythonExtension-Start','Start',-1)
- return True
- class labkeySlicerPythonExtensionTest(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_labkeySlicerPythonExtension1()
- def test_labkeySlicerPythonExtension1(self):
- """ Ideally you should have several levels of tests. At the lowest level
- tests sould 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:
- print('Requesting download %s from %s...\n' % (name, url))
- urllib.urlretrieve(url, filePath)
- if loader:
- print('Loading %s...\n' % (name,))
- loader(filePath)
- self.delayDisplay('Finished with download and loading\n')
- volumeNode = slicer.util.getNode(pattern="FA")
- logic = labkeySlicerPythonExtensionLogic()
- self.assertTrue( logic.hasImageData(volumeNode) )
- self.delayDisplay('Test passed!')
|