iraemmBrowser.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. import os
  2. import unittest
  3. from __main__ import vtk, qt, ctk, slicer
  4. from slicer.ScriptedLoadableModule import *
  5. import slicerNetwork
  6. import loadDicom
  7. import json
  8. #
  9. # labkeySlicerPythonExtension
  10. #
  11. class iraemmBrowser(ScriptedLoadableModule):
  12. """Uses ScriptedLoadableModule base class, available at:
  13. https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py
  14. """
  15. def __init__(self, parent):
  16. ScriptedLoadableModule.__init__(self, parent)
  17. self.parent.title = "irAEMM Browser" # TODO make this more human readable by adding spaces
  18. self.parent.categories = ["LabKey"]
  19. self.parent.dependencies = []
  20. self.parent.contributors = ["Andrej Studen (UL/FMF)"] # replace with "Firstname Lastname (Organization)"
  21. self.parent.helpText = """
  22. Interface to irAEMM files in LabKey
  23. """
  24. self.parent.acknowledgementText = """
  25. Developed within the medical physics research programme of the Slovenian research agency.
  26. """ # replace with organization, grant and thanks.
  27. #
  28. # labkeySlicerPythonExtensionWidget
  29. #
  30. class iraemmBrowserWidget(ScriptedLoadableModuleWidget):
  31. """Uses ScriptedLoadableModuleWidget base class, available at:
  32. https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py
  33. """
  34. def setup(self):
  35. print("Setting up iraemmBrowserWidget")
  36. ScriptedLoadableModuleWidget.setup(self)
  37. # Instantiate and connect widgets ...
  38. self.network=slicerNetwork.labkeyURIHandler()
  39. fconfig=os.path.join(os.path.expanduser('~'),'.labkey','onko-nix.json')
  40. self.network.parseConfig(fconfig)
  41. self.network.initRemote()
  42. self.project="iPNUMMretro/Study"
  43. self.dataset="Imaging1"
  44. self.logic=iraemmBrowserLogic(self)
  45. ds=self.network.filterDataset(self.project,self.dataset,[])
  46. ids=[row['PatientId'] for row in ds['rows']]
  47. ids=list(set(ids))
  48. #
  49. # Parameters Area
  50. #
  51. connectionCollapsibleButton = ctk.ctkCollapsibleButton()
  52. connectionCollapsibleButton.text = "Patients"
  53. self.layout.addWidget(connectionCollapsibleButton)
  54. connectionFormLayout = qt.QFormLayout(connectionCollapsibleButton)
  55. self.patientList=qt.QComboBox()
  56. for id in ids:
  57. self.patientList.addItem(id)
  58. self.patientList.currentIndexChanged.connect(self.onPatientListChanged)
  59. connectionFormLayout.addRow("Patient:",self.patientList)
  60. self.visitList=qt.QComboBox()
  61. self.visitList.currentIndexChanged.connect(self.onVisitListChanged)
  62. connectionFormLayout.addRow("Visit:",self.visitList)
  63. self.ctCode=qt.QLabel("ctCode")
  64. connectionFormLayout.addRow("CT:",self.ctCode)
  65. self.petCode=qt.QLabel("petCode")
  66. connectionFormLayout.addRow("PET:",self.petCode)
  67. self.patientLoad=qt.QPushButton("Load")
  68. self.patientLoad.clicked.connect(self.onPatientLoadButtonClicked)
  69. connectionFormLayout.addRow("Load patient",self.patientLoad)
  70. self.keepCached=qt.QCheckBox("keep Cached")
  71. self.keepCached.setChecked(1)
  72. connectionFormLayout.addRow("Keep cached",self.keepCached)
  73. #set to a defined state
  74. self.onPatientListChanged(0)
  75. def onPatientListChanged(self,i):
  76. idFilter={'variable':'PatientId','value':self.patientList.currentText,'oper':'eq'}
  77. ds=self.network.filterDataset(self.project,self.dataset, [idFilter])
  78. seq=[int(row['SequenceNum']) for row in ds['rows']]
  79. self.visitList.clear()
  80. for s in seq:
  81. self.visitList.addItem("Visit "+str(s))
  82. self.onVisitListChanged(0)
  83. def onVisitListChanged(self,i):
  84. try:
  85. s=self.visitList.currentText.split(' ')[1]
  86. except IndexError:
  87. return
  88. print("Visit: Selected item: {}->{}".format(i,s))
  89. idFilter={'variable':'PatientId','value':self.patientList.currentText,'oper':'eq'}
  90. sFilter={'variable':'SequenceNum','value':s,'oper':'eq'}
  91. ds=self.network.filterDataset(self.project,self.dataset,[idFilter,sFilter])
  92. if not len(ds['rows'])==1:
  93. print("Found incorrect number {} of matches for [{}]/[{}]".format(len(ds['rows']),\
  94. self.patientList.currentText,s))
  95. row=ds['rows'][0]
  96. #copy row properties for data access
  97. self.currentRow=row
  98. self.petCode.setText(row['petResampled'])
  99. self.ctCode.setText(row['ctResampled'])
  100. def onPatientLoadButtonClicked(self):
  101. print("Load")
  102. #delegate loading to logic
  103. #try:
  104. self.logic.loadImage(self.currentRow,self.keepCached.isChecked())
  105. #except AttributeError:
  106. # print("Missing current row")
  107. # return
  108. def cleanup(self):
  109. pass
  110. #
  111. # irAEMMBrowserLogic
  112. #
  113. class iraemmBrowserLogic(ScriptedLoadableModuleLogic):
  114. """This class should implement all the actual
  115. computation done by your module. The interface
  116. should be such that other python code can import
  117. this class and make use of the functionality without
  118. requiring an instance of the Widget.
  119. Uses ScriptedLoadableModuleLogic base class, available at:
  120. https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py
  121. """
  122. def __init__(self,parent=None):
  123. ScriptedLoadableModuleLogic.__init__(self, parent)
  124. if not parent==None:
  125. #assume parent has the network set up
  126. self.parent=parent
  127. self.net=parent.network
  128. self.project=parent.project
  129. def setLabkeyInterface(self,net):
  130. #additional way of setting the labkey network interface
  131. #if no parent was provided in logic initialization (stand-alone mode)
  132. self.net=net
  133. def setLabkeyProject(self,project):
  134. self.project=project
  135. def loadImage(self,row,keepCached):
  136. #fields={'ctResampled':True,'petResampled':False}
  137. fields=['ctResampled','petResampled']
  138. relativePaths={x:self.project+'/@files/preprocessedImages/'\
  139. +row['patientCode']+'/'+row['visitCode']+'/'+row[x]\
  140. for x in fields}
  141. volumeNode={}
  142. for f in relativePaths:
  143. p=relativePaths[f]
  144. labkeyPath=self.net.GetLabkeyPathFromRelativePath(p)
  145. rp=self.net.head(labkeyPath)
  146. if not rp.code==200:
  147. print("Failed to get {}".format(labkeyPath))
  148. continue
  149. #pushes it to background
  150. volumeNode[f]=self.net.loadNode(p,'VolumeFile',returnNode=True,keepCached=keepCached)
  151. slicer.util.setSliceViewerLayers(background=volumeNode['ctResampled'],\
  152. foreground=volumeNode['petResampled'],foregroundOpacity=0.5,fit=True)
  153. class irAEMMBrowserTest(ScriptedLoadableModuleTest):
  154. """
  155. This is the test case for your scripted module.
  156. Uses ScriptedLoadableModuleTest base class, available at:
  157. https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py
  158. """
  159. def setUp(self):
  160. """ Do whatever is needed to reset the state - typically a scene clear will be enough.
  161. """
  162. slicer.mrmlScene.Clear(0)
  163. def runTest(self):
  164. """Run as few or as many tests as needed here.
  165. """
  166. self.setUp()
  167. self.test_irAEMMBrowser()
  168. def test_irAEMMBrowser(self):
  169. """ Ideally you should have several levels of tests. At the lowest level
  170. tests sould exercise the functionality of the logic with different inputs
  171. (both valid and invalid). At higher levels your tests should emulate the
  172. way the user would interact with your code and confirm that it still works
  173. the way you intended.
  174. One of the most important features of the tests is that it should alert other
  175. developers when their changes will have an impact on the behavior of your
  176. module. For example, if a developer removes a feature that you depend on,
  177. your test should break so they know that the feature is needed.
  178. """
  179. self.delayDisplay("Starting the test")
  180. #
  181. # first, get some data
  182. #