labkeySlicerPythonExtension.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. import os
  2. import unittest
  3. from __main__ import vtk, qt, ctk, slicer
  4. from slicer.ScriptedLoadableModule import *
  5. import slicerNetwork
  6. #
  7. # labkeySlicerPythonExtension
  8. #
  9. class labkeySlicerPythonExtension(ScriptedLoadableModule):
  10. """Uses ScriptedLoadableModule base class, available at:
  11. https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py
  12. """
  13. def __init__(self, parent):
  14. ScriptedLoadableModule.__init__(self, parent)
  15. self.parent.title = "labkeySlicerPythonExtension" # TODO make this more human readable by adding spaces
  16. self.parent.categories = ["Examples"]
  17. self.parent.dependencies = []
  18. self.parent.contributors = ["Andrej Studen (UL/FMF)"] # replace with "Firstname Lastname (Organization)"
  19. self.parent.helpText = """
  20. Labkey interface to slicer
  21. """
  22. self.parent.acknowledgementText = """
  23. Developed within the medical physics research programme of the Slovenian research agency.
  24. """ # replace with organization, grant and thanks.
  25. #
  26. # labkeySlicerPythonExtensionWidget
  27. #
  28. class labkeySlicerPythonExtensionWidget(ScriptedLoadableModuleWidget):
  29. """Uses ScriptedLoadableModuleWidget base class, available at:
  30. https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py
  31. """
  32. def setup(self):
  33. ScriptedLoadableModuleWidget.setup(self)
  34. # Instantiate and connect widgets ...
  35. self.logic=labkeySlicerPythonExtensionLogic(self)
  36. self.network=slicerNetwork.labkeyURIHandler()
  37. #
  38. # Parameters Area
  39. #
  40. connectionCollapsibleButton = ctk.ctkCollapsibleButton()
  41. connectionCollapsibleButton.text = "Connection"
  42. self.layout.addWidget(connectionCollapsibleButton)
  43. connectionFormLayout = qt.QFormLayout(connectionCollapsibleButton)
  44. self.serverURL=qt.QLineEdit("https://merlin.fmf.uni-lj.si")
  45. connectionFormLayout.addRow("Server: ", self.serverURL)
  46. try:
  47. self.startDir=os.path.join(os.environ['HOME'],"temp/crt")
  48. except:
  49. fhome=os.environ['HOMEDRIVE']+os.environ['HOMEPATH']
  50. self.startDir=os.path.join(fhome,"temp")
  51. self.userCertButton=qt.QPushButton("Load")
  52. self.userCertButton.toolTip="Load user certificate (crt)"
  53. self.userCertButton.connect('clicked(bool)',self.onUserCertButtonClicked)
  54. connectionFormLayout.addRow("User certificate:",self.userCertButton)
  55. self.privateKeyButton=qt.QPushButton("Load")
  56. self.privateKeyButton.toolTip="Load private key"
  57. self.privateKeyButton.connect('clicked(bool)',self.onPrivateKeyButtonClicked)
  58. connectionFormLayout.addRow("Private key:",self.privateKeyButton)
  59. self.caCertButton=qt.QPushButton("Load")
  60. self.caCertButton.toolTip="Load CA certificate (crt)"
  61. self.caCertButton.connect('clicked(bool)',self.onCaCertButtonClicked)
  62. connectionFormLayout.addRow("CA certificate:",self.caCertButton)
  63. self.auth_pwd=''
  64. self.connectButton=qt.QPushButton("Connect")
  65. self.connectButton.toolTip="Connect to the server"
  66. self.connectButton.connect('clicked(bool)',self.onConnectButtonClicked)
  67. connectionFormLayout.addRow("Connection:",self.connectButton)
  68. fileDialogCollapsibleButton = ctk.ctkCollapsibleButton()
  69. fileDialogCollapsibleButton.text = "Remote files"
  70. self.layout.addWidget(fileDialogCollapsibleButton)
  71. # Layout within the dummy collapsible button
  72. fileDialogFormLayout = qt.QFormLayout(fileDialogCollapsibleButton)
  73. #add item list for each found file/directory
  74. self.fileList=qt.QListWidget(parent=self)
  75. self.fileList.toolTip="Select remote file"
  76. self.fileList.itemDoubleClicked.connect(self.onFileListDoubleClicked)
  77. self.currentRemoteDir=''
  78. #add dummy entry
  79. items=('.','..')
  80. self.populateFileList(items)
  81. fileDialogFormLayout.addWidget(self.fileList)
  82. #add selected file display
  83. self.selectedFile=qt.QLineEdit(parent=self)
  84. self.selectedFile.toolTip="Selected file"
  85. fileDialogFormLayout.addRow("Selected file :",self.selectedFile)
  86. #add possible file Content
  87. self.fileTypeSelector=qt.QComboBox()
  88. self.fileTypeSelector.toolTip="Select file type"
  89. items=self.network.fileTypesAvailable()
  90. self.populateFileTypeSelector(items)
  91. fileDialogFormLayout.addRow("File type :",self.fileTypeSelector)
  92. self.keepCachedFileCheckBox=qt.QCheckBox("keep cached file")
  93. self.keepCachedFileCheckBox.toolTip="Toggle local storage of labkey files"
  94. self.keepCachedFileCheckBox.setChecked(True)
  95. fileDialogFormLayout.addRow("Manage cache :",self.keepCachedFileCheckBox)
  96. loadFileButton=qt.QPushButton("Load file")
  97. loadFileButton.toolTip="Load file"
  98. loadFileButton.clicked.connect(self.onLoadFileButtonClicked)
  99. fileDialogFormLayout.addRow("Action :",loadFileButton)
  100. loadDirButton=qt.QPushButton("Load directory")
  101. loadDirButton.toolTip="Load directory"
  102. loadDirButton.clicked.connect(self.onLoadDirButtonClicked)
  103. fileDialogFormLayout.addRow("Action :",loadDirButton)
  104. def populateFileList(self,items):
  105. self.fileList.clear()
  106. for it_text in items:
  107. item=qt.QListWidgetItem(self.fileList)
  108. item.setText(it_text)
  109. item.setIcon(qt.QIcon(it_text))
  110. def populateFileTypeSelector(self,items):
  111. for item in items:
  112. self.fileTypeSelector.addItem(item)
  113. def cleanup(self):
  114. pass
  115. def onSelect(self):
  116. self.applyButton.enabled = self.inputSelector.currentNode() and self.outputSelector.currentNode()
  117. def onApplyButton(self):
  118. #logic = labkeySlicerPythonExtensionLogic()
  119. #enableScreenshotsFlag = self.enableScreenshotsFlagCheckBox.checked
  120. #screenshotScaleFactor = int(self.screenshotScaleFactorSliderWidget.value)
  121. print("Run the algorithm")
  122. #logic.run(self.inputSelector.currentNode(), self.outputSelector.currentNode(), enableScreenshotsFlag,screenshotScaleFactor)
  123. def onUserCertButtonClicked(self):
  124. filename=qt.QFileDialog.getOpenFileName(None,'Open user certificate',
  125. self.startDir, '*.crt')
  126. #pwd=qt.QInputDialog.getText(None,'Certificate password',
  127. # 'Enter certificate password',qt.QLineEdit.Password)
  128. if not(filename) :
  129. print "No file selected"
  130. return
  131. f=qt.QFile(filename)
  132. if not (f.open(qt.QIODevice.ReadOnly)) :
  133. print "Could not open file"
  134. return
  135. certList=qt.QSslCertificate.fromPath(filename)
  136. if len(certList) < 1:
  137. print "Troubles parsing {0}".format(filename)
  138. return
  139. self.logic.cert=qt.QSslCertificate(f)
  140. print "cert.isNull()={0}".format(self.logic.cert.isNull())
  141. self.userCertButton.setText(filename)
  142. def onPrivateKeyButtonClicked(self):
  143. filename=qt.QFileDialog.getOpenFileName(None,'Open private key',
  144. self.startDir, '*.key')
  145. if not (filename) :
  146. print "No file selected"
  147. return
  148. f=qt.QFile(filename)
  149. if not (f.open(qt.QIODevice.ReadOnly)) :
  150. print "Could not open file"
  151. return
  152. self.pwd=qt.QInputDialog.getText(None,'Private key password',
  153. 'Enter key password',qt.QLineEdit.Password)
  154. self.logic.key=qt.QSslKey(f,qt.QSsl.Rsa,qt.QSsl.Pem,qt.QSsl.PrivateKey,
  155. str(self.pwd))
  156. self.privateKeyButton.setText(filename)
  157. def onCaCertButtonClicked(self):
  158. filename=qt.QFileDialog.getOpenFileName(None,'Open authority certificate',
  159. self.startDir, '*.crt')
  160. if not(filename) :
  161. print "No file selected"
  162. return
  163. f=qt.QFile(filename)
  164. if not (f.open(qt.QIODevice.ReadOnly)) :
  165. print "Could not open file"
  166. return
  167. certList=qt.QSslCertificate.fromPath(filename)
  168. if len(certList) < 1:
  169. print "Troubles parsing {0}".format(filename)
  170. return
  171. self.logic.caCert=qt.QSslCertificate(f)#certList[0]
  172. self.caCertButton.setText(filename)
  173. def onConnectButtonClicked(self):
  174. self.network.configureSSL(
  175. self.userCertButton.text,
  176. self.privateKeyButton.text,
  177. self.pwd,
  178. self.caCertButton.text
  179. )
  180. uname=str(self.logic.cert.subjectInfo("emailAddress"))
  181. uname=qt.QInputDialog.getText(None,
  182. "Labkey credentials","Enter username",qt.QLineEdit.Normal,uname)
  183. self.auth_pwd=qt.QInputDialog.getText(None,
  184. "Labkey credentials","Enter password",qt.QLineEdit.Password,self.auth_pwd)
  185. self.network.connectRemote(str(self.serverURL.text),uname,self.auth_pwd)
  186. def onFileListDoubleClicked(self,item):
  187. if item == None:
  188. print "Selected items: None"
  189. return
  190. iText=item.text()
  191. print "Selected items: {0} ".format(iText)
  192. #this is hard -> compose path string from currentRemoteDir and selection
  193. if item.text().find('..')==0:
  194. #one up
  195. idx=self.currentRemoteDir.rfind('/')
  196. if idx<0:
  197. self.currentRemoteDir=''
  198. else:
  199. self.currentRemoteDir=self.currentRemoteDir[:idx]
  200. elif item.text().find('.')==0:
  201. pass
  202. else:
  203. if len(self.currentRemoteDir)>0:
  204. self.currentRemoteDir+='/'
  205. self.currentRemoteDir+=item.text()
  206. print "Listing {0}".format(self.currentRemoteDir)
  207. flist=self.network.toRelativePath(
  208. self.network.listDir(self.currentRemoteDir))
  209. print "Got"
  210. print flist
  211. flist.insert(0,'..')
  212. flist.insert(0,'.')
  213. self.populateFileList(flist)
  214. self.selectedFile.setText(self.currentRemoteDir)
  215. def onLoadFileButtonClicked(self):
  216. properties={}
  217. properties['keepCachedFile']=self.keepCachedFileCheckBox.isChecked()
  218. self.network.loadNodeFromFile(self.selectedFile.text,
  219. self.fileTypeSelector.currentText, properties)
  220. def onLoadDirButtonClicked(self):
  221. self.network.loadDir(self.selectedFile.text)
  222. #
  223. # labkeySlicerPythonExtensionLogic
  224. #
  225. class labkeySlicerPythonExtensionLogic(ScriptedLoadableModuleLogic):
  226. """This class should implement all the actual
  227. computation done by your module. The interface
  228. should be such that other python code can import
  229. this class and make use of the functionality without
  230. requiring an instance of the Widget.
  231. Uses ScriptedLoadableModuleLogic base class, available at:
  232. https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py
  233. """
  234. def __init__(self,parent):
  235. ScriptedLoadableModuleLogic.__init__(self, parent)
  236. self.qnam=qt.QNetworkAccessManager()
  237. def hasImageData(self,volumeNode):
  238. """This is a dummy logic method that
  239. returns true if the passed in volume
  240. node has valid image data
  241. """
  242. if not volumeNode:
  243. print('no volume node')
  244. return False
  245. if volumeNode.GetImageData() == None:
  246. print('no image data')
  247. return False
  248. return True
  249. def takeScreenshot(self,name,description,type=-1):
  250. # show the message even if not taking a screen shot
  251. self.delayDisplay(description)
  252. if self.enableScreenshots == 0:
  253. return
  254. lm = slicer.app.layoutManager()
  255. # switch on the type to get the requested window
  256. widget = 0
  257. if type == slicer.qMRMLScreenShotDialog.FullLayout:
  258. # full layout
  259. widget = lm.viewport()
  260. elif type == slicer.qMRMLScreenShotDialog.ThreeD:
  261. # just the 3D window
  262. widget = lm.threeDWidget(0).threeDView()
  263. elif type == slicer.qMRMLScreenShotDialog.Red:
  264. # red slice window
  265. widget = lm.sliceWidget("Red")
  266. elif type == slicer.qMRMLScreenShotDialog.Yellow:
  267. # yellow slice window
  268. widget = lm.sliceWidget("Yellow")
  269. elif type == slicer.qMRMLScreenShotDialog.Green:
  270. # green slice window
  271. widget = lm.sliceWidget("Green")
  272. else:
  273. # default to using the full window
  274. widget = slicer.util.mainWindow()
  275. # reset the type so that the node is set correctly
  276. type = slicer.qMRMLScreenShotDialog.FullLayout
  277. # grab and convert to vtk image data
  278. qpixMap = qt.QPixmap().grabWidget(widget)
  279. qimage = qpixMap.toImage()
  280. imageData = vtk.vtkImageData()
  281. slicer.qMRMLUtils().qImageToVtkImageData(qimage,imageData)
  282. annotationLogic = slicer.modules.annotations.logic()
  283. annotationLogic.CreateSnapShot(name, description, type, self.screenshotScaleFactor, imageData)
  284. def run(self,inputVolume,outputVolume,enableScreenshots=0,screenshotScaleFactor=1):
  285. """
  286. Run the actual algorithm
  287. """
  288. self.delayDisplay('Running the aglorithm')
  289. self.enableScreenshots = enableScreenshots
  290. self.screenshotScaleFactor = screenshotScaleFactor
  291. self.takeScreenshot('labkeySlicerPythonExtension-Start','Start',-1)
  292. return True
  293. class labkeySlicerPythonExtensionTest(ScriptedLoadableModuleTest):
  294. """
  295. This is the test case for your scripted module.
  296. Uses ScriptedLoadableModuleTest base class, available at:
  297. https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py
  298. """
  299. def setUp(self):
  300. """ Do whatever is needed to reset the state - typically a scene clear will be enough.
  301. """
  302. slicer.mrmlScene.Clear(0)
  303. def runTest(self):
  304. """Run as few or as many tests as needed here.
  305. """
  306. self.setUp()
  307. self.test_labkeySlicerPythonExtension1()
  308. def test_labkeySlicerPythonExtension1(self):
  309. """ Ideally you should have several levels of tests. At the lowest level
  310. tests sould exercise the functionality of the logic with different inputs
  311. (both valid and invalid). At higher levels your tests should emulate the
  312. way the user would interact with your code and confirm that it still works
  313. the way you intended.
  314. One of the most important features of the tests is that it should alert other
  315. developers when their changes will have an impact on the behavior of your
  316. module. For example, if a developer removes a feature that you depend on,
  317. your test should break so they know that the feature is needed.
  318. """
  319. self.delayDisplay("Starting the test")
  320. #
  321. # first, get some data
  322. #
  323. import urllib
  324. downloads = (
  325. ('http://slicer.kitware.com/midas3/download?items=5767', 'FA.nrrd', slicer.util.loadVolume),
  326. )
  327. for url,name,loader in downloads:
  328. filePath = slicer.app.temporaryPath + '/' + name
  329. if not os.path.exists(filePath) or os.stat(filePath).st_size == 0:
  330. print('Requesting download %s from %s...\n' % (name, url))
  331. urllib.urlretrieve(url, filePath)
  332. if loader:
  333. print('Loading %s...\n' % (name,))
  334. loader(filePath)
  335. self.delayDisplay('Finished with download and loading\n')
  336. volumeNode = slicer.util.getNode(pattern="FA")
  337. logic = labkeySlicerPythonExtensionLogic()
  338. self.assertTrue( logic.hasImageData(volumeNode) )
  339. self.delayDisplay('Test passed!')