|
@@ -9,6 +9,7 @@ import nixModule
|
|
|
import pathlib
|
|
|
import chardet
|
|
|
import re
|
|
|
+import functools
|
|
|
#
|
|
|
# labkeySlicerPythonExtension
|
|
|
#
|
|
@@ -51,7 +52,7 @@ class segmentationBrowserWidget(ScriptedLoadableModuleWidget):
|
|
|
self.addInfoSection()
|
|
|
self.addSetupSection()
|
|
|
self.addPatientsSelector()
|
|
|
- #self.addSegmentEditor()
|
|
|
+ self.addSegmentViewer()
|
|
|
self.addWindowManipulator()
|
|
|
|
|
|
def addInfoSection(self):
|
|
@@ -223,21 +224,20 @@ class segmentationBrowserWidget(ScriptedLoadableModuleWidget):
|
|
|
submitFrame.setStyleSheet("background-color:rgba(220,215,180,45)")
|
|
|
self.reviewBoxLayout.addWidget(submitFrame)
|
|
|
|
|
|
- def addSegmentEditor(self):
|
|
|
+ def addSegmentViewer(self):
|
|
|
#not used (for review)
|
|
|
editorCollapsibleButton = ctk.ctkCollapsibleButton()
|
|
|
- editorCollapsibleButton.text = "Segment Editor"
|
|
|
+ editorCollapsibleButton.text = "Segment Viewer"
|
|
|
self.layout.addWidget(editorCollapsibleButton)
|
|
|
- hLayout=qt.QVBoxLayout(editorCollapsibleButton)
|
|
|
+ self.vSegViewLayout=qt.QVBoxLayout(editorCollapsibleButton)
|
|
|
|
|
|
- self.segmentEditorWidget=slicer.qMRMLSegmentEditorWidget()
|
|
|
- hLayout.addWidget(self.segmentEditorWidget)
|
|
|
+ segList=' '.join(self.logic.segmentList)
|
|
|
+ label=qt.QLabel(" {}".format(segList))
|
|
|
+ self.vSegViewLayout.addWidget(label)
|
|
|
|
|
|
- self.segmentEditorWidget.setMRMLScene(slicer.mrmlScene)
|
|
|
- segEditorNode=slicer.vtkMRMLSegmentEditorNode()
|
|
|
- slicer.mrmlScene.AddNode(segEditorNode)
|
|
|
- self.segmentEditorWidget.setMRMLSegmentEditorNode(segEditorNode)
|
|
|
-
|
|
|
+ self.segList=[]
|
|
|
+
|
|
|
+
|
|
|
def addWindowManipulator(self):
|
|
|
windowManipulatorCollapsibleButton=ctk.ctkCollapsibleButton()
|
|
|
windowManipulatorCollapsibleButton.text="CT Window Manipulator"
|
|
@@ -359,24 +359,88 @@ class segmentationBrowserWidget(ScriptedLoadableModuleWidget):
|
|
|
self.patientList.addItem(id)
|
|
|
|
|
|
def onPatientLoadButtonClicked(self):
|
|
|
+ #clear first
|
|
|
+ self.logic.clearVolumesAndSegmentations()
|
|
|
print("Load")
|
|
|
#delegate loading to logic
|
|
|
self.logic.loadImages(self.currentRow,self.keepCached.isChecked())
|
|
|
self.logic.loadSegmentations(self.currentRow)
|
|
|
- #self.setSegmentEditor()
|
|
|
+ self.updateSegmentationViewer(self.logic.segmentationRows)
|
|
|
#self.logic.loadReview(self.currentRow)
|
|
|
#self.logic.loadAE(self.currentRow)
|
|
|
|
|
|
#self.onReviewSegmentChanged()
|
|
|
|
|
|
- def setSegmentEditor(self):
|
|
|
- #not used
|
|
|
- #use current row to set segment in segment editor
|
|
|
- self.segmentEditorWidget.setSegmentationNode(
|
|
|
- self.logic.volumeNode['Segmentation'])
|
|
|
- self.segmentEditorWidget.setMasterVolumeNode(
|
|
|
- self.logic.volumeNode['PET'])
|
|
|
-
|
|
|
+ def generateSegmentationView(self):
|
|
|
+ s=qt.QHBoxLayout()
|
|
|
+ nm=qt.QLabel("name")
|
|
|
+ s.addWidget(nm)
|
|
|
+ for g in self.logic.segmentList:
|
|
|
+ t=qt.QCheckBox()
|
|
|
+ s.addWidget(t)
|
|
|
+ return s
|
|
|
+
|
|
|
+ def setSegmentationViewName(self,s,name):
|
|
|
+ label=s.itemAt(0).widget()
|
|
|
+ label.setText(name)
|
|
|
+
|
|
|
+ def getWidgetsFromLayout(self,s):
|
|
|
+ return [s.itemAt(i).widget() for i in range(s.count())]
|
|
|
+
|
|
|
+ def hideLayout(self,s):
|
|
|
+ for w in self.getWidgetsFromLayout(s):
|
|
|
+ w.hide()
|
|
|
+
|
|
|
+ def disconnect(self,s):
|
|
|
+ for w in self.getWidgetsFromLayout(s)[1:]:
|
|
|
+ try:
|
|
|
+ w.stateChanged.disconnect()
|
|
|
+ except TypeError:
|
|
|
+ continue
|
|
|
+
|
|
|
+
|
|
|
+ def showLayout(self,s):
|
|
|
+ for w in self.getWidgetsFromLayout(s):
|
|
|
+ w.show()
|
|
|
+
|
|
|
+ def refreshCallbacks(self,s,entry):
|
|
|
+ w=self.getWidgetsFromLayout(s)
|
|
|
+ for i in range(len(self.logic.segmentList)):
|
|
|
+ w[i+1].stateChanged.connect(functools.partial(self.onSegmentationViewChanged,
|
|
|
+ latestFile=entry['latestFile'],
|
|
|
+ segment=self.logic.segmentList[i]))
|
|
|
+
|
|
|
+
|
|
|
+ def getUserMap(self):
|
|
|
+ users=self.logic.lookups['core:Users']
|
|
|
+ #print('{}'.format(users))
|
|
|
+ return users
|
|
|
+
|
|
|
+ def updateSegmentationViewer(self,rows):
|
|
|
+ users=self.getUserMap()
|
|
|
+ for s in self.segList:
|
|
|
+ self.hideLayout(s)
|
|
|
+ self.disconnect(s)
|
|
|
+ #can't hide a layout
|
|
|
+
|
|
|
+ #s.hide()
|
|
|
+ i=0
|
|
|
+ for r in rows:
|
|
|
+ try:
|
|
|
+ s=self.segList[i]
|
|
|
+ except IndexError:
|
|
|
+ #assume we only have to add one (since we iterate sequentially)
|
|
|
+ self.segList.append(self.generateSegmentationView())
|
|
|
+ s=self.segList[i]
|
|
|
+ self.vSegViewLayout.addLayout(s)
|
|
|
+ self.showLayout(s)
|
|
|
+ self.setSegmentationViewName(s,users[r['User']])
|
|
|
+ self.refreshCallbacks(s,r)
|
|
|
+ i+=1
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
def onReviewSegmentChanged(self):
|
|
|
pass
|
|
|
|
|
@@ -402,6 +466,10 @@ class segmentationBrowserWidget(ScriptedLoadableModuleWidget):
|
|
|
def onCtLungButtonClicked(self):
|
|
|
self.logic.setWindow('CT:lung')
|
|
|
|
|
|
+ def onSegmentationViewChanged(self,state,latestFile,segment):
|
|
|
+ print('onSegmentationViewChanged {} {} {} '.format(latestFile,segment,state))
|
|
|
+ self.logic.toggleSegmentVisibility(latestFile,segment,state>0)
|
|
|
+
|
|
|
def cleanup(self):
|
|
|
pass
|
|
|
|
|
@@ -619,9 +687,13 @@ class segmentationBrowserLogic(ScriptedLoadableModuleLogic):
|
|
|
if idx=="Segmentation":
|
|
|
filetype='SegmentationFile'
|
|
|
#properties["labelmap"]=1
|
|
|
-
|
|
|
+
|
|
|
+ try:
|
|
|
+ volumeIdx=iData['volumeIdx']
|
|
|
+ except KeyError:
|
|
|
+ volumeIdx=idx
|
|
|
|
|
|
- self.volumeNode[idx]=slicer.util.loadNodeFromFile(localPath,
|
|
|
+ self.volumeNode[volumeIdx]=slicer.util.loadNodeFromFile(localPath,
|
|
|
filetype=filetype,properties=properties)
|
|
|
|
|
|
if not keepCached:
|
|
@@ -664,34 +736,38 @@ class segmentationBrowserLogic(ScriptedLoadableModuleLogic):
|
|
|
'visitCode':row['visitCode']}
|
|
|
ds=self.getDataset(name='SegmentationsMaster',
|
|
|
dbFilter=dbFilter)
|
|
|
- if len(ds['rows'])<0:
|
|
|
+ self.segmentationRows=ds['rows']
|
|
|
+ if len(self.segmentationRows)<0:
|
|
|
print('No segmentations for {}'.format(dbFilter))
|
|
|
return
|
|
|
- for r in ds['rows']:
|
|
|
+ for r in self.segmentationRows:
|
|
|
#update self.segmentationEntry
|
|
|
#self.segmentationEntry=ds['rows'][0]
|
|
|
#self.segmentationEntry['origin']='database'
|
|
|
#if loadFile:
|
|
|
self.loadSegmentationFromEntry(r)
|
|
|
- return
|
|
|
+ #make invisile by default
|
|
|
+ for s in self.segmentList:
|
|
|
+ self.toggleSegmentVisibility(r['latestFile'],s,False)
|
|
|
print('load Segmentation: done')
|
|
|
#create new segmentation
|
|
|
#self.createSegmentation(row)
|
|
|
|
|
|
- def getSegmentationPath(self):
|
|
|
+ def getSegmentationPath(self,entry):
|
|
|
path=[self.isetup['imageDir'],
|
|
|
- self.segmentationEntry['patientCode'],
|
|
|
- self.segmentationEntry['visitCode']]
|
|
|
+ entry['patientCode'],
|
|
|
+ entry['visitCode']]
|
|
|
path.append('Segmentations')
|
|
|
return path
|
|
|
|
|
|
def loadSegmentationFromEntry(self,r):
|
|
|
#compile path
|
|
|
entry=r
|
|
|
- path=self.getSegmentationPath()
|
|
|
+ path=self.getSegmentationPath(entry)
|
|
|
path.append(entry['latestFile'])
|
|
|
iData={'idx':'Segmentation','path':path,
|
|
|
- 'keepCached':0,'dataset':'SegmentationsMaster'}
|
|
|
+ 'keepCached':0,'dataset':'SegmentationsMaster',
|
|
|
+ 'volumeIdx':entry['latestFile']}
|
|
|
self.loadImage(iData)
|
|
|
#look for missing segments
|
|
|
return
|
|
@@ -710,6 +786,17 @@ class segmentationBrowserLogic(ScriptedLoadableModuleLogic):
|
|
|
print(s)
|
|
|
print('Done')
|
|
|
|
|
|
+ def toggleSegmentVisibility(self,latestFile,segmentId,visible):
|
|
|
+ segNode=self.volumeNode[latestFile]
|
|
|
+ if segNode==None:
|
|
|
+ print('Could not find segNode')
|
|
|
+ return
|
|
|
+ seg=segNode.GetSegmentation()
|
|
|
+ segment=seg.GetSegment(segmentId)
|
|
|
+ if segment==None:
|
|
|
+ print('Could not find segment {}'.format(segmentId))
|
|
|
+ return
|
|
|
+ segNode.GetDisplayNode().SetSegmentVisibility(segmentId, visible)
|
|
|
|
|
|
|
|
|
|
|
@@ -806,6 +893,10 @@ class segmentationBrowserLogic(ScriptedLoadableModuleLogic):
|
|
|
nodes=slicer.util.getNodesByClass("vtkMRMLVolumeNode")
|
|
|
nodes.extend(slicer.util.getNodesByClass("vtkMRMLSegmentationNode"))
|
|
|
res=[slicer.mrmlScene.RemoveNode(f) for f in nodes]
|
|
|
+ try:
|
|
|
+ self.volumeNode.clear()
|
|
|
+ except AttributeError:
|
|
|
+ pass
|
|
|
#self.segmentationNode=None
|
|
|
#self.reviewResult={}
|
|
|
#self.aeList={}
|