import os import unittest from __main__ import vtk, qt, ctk, slicer from slicer.ScriptedLoadableModule import * import json import datetime import sys import nixModule import pathlib import chardet import re import functools # # labkeySlicerPythonExtension # class segmentationBrowser(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 = "segmentation Browser" # TODO make this more human readable by adding spaces self.parent.categories = ["LabKey"] self.parent.dependencies = [] self.parent.contributors = ["Andrej Studen (UL/FMF)"] # replace with "Firstname Lastname (Organization)" self.parent.helpText = """ Interface to stored segmentation files in LabKey """ self.parent.acknowledgementText = """ Developed within the medical physics research programme of the Slovenian research agency. """ # replace with organization, grant and thanks. # # labkeySlicerPythonExtensionWidget # class segmentationBrowserWidget(ScriptedLoadableModuleWidget): """Uses ScriptedLoadableModuleWidget base class, available at: https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py """ def setup(self): print("Setting up segmentationBrowserWidget") ScriptedLoadableModuleWidget.setup(self) # Instantiate and connect widgets ... self.logic=segmentationBrowserLogic(self) self.addInfoSection() self.addSetupSection() self.addPatientsSelector() self.addSegmentViewer() self.addWindowManipulator() def addInfoSection(self): #a python overview of json settings infoCollapsibleButton = ctk.ctkCollapsibleButton() infoCollapsibleButton.text = "Info" self.layout.addWidget(infoCollapsibleButton) infoLayout = qt.QFormLayout(infoCollapsibleButton) self.participantField=qt.QLabel("PatientId") infoLayout.addRow("Participant field:",self.participantField) self.ctField=qt.QLabel("ctResampled") infoLayout.addRow("Data field (CT):",self.ctField) self.petField=qt.QLabel("petResampled") infoLayout.addRow("Data field (PET):",self.petField) self.userField=qt.QLabel("Loading") infoLayout.addRow("User",self.userField) self.idField=qt.QLabel("Loading") infoLayout.addRow("ID",self.idField) #Add logic at some point #self.logic=segmentationBrowserLogic(self) def addPatientsSelector(self): # # Patients Area # patientsCollapsibleButton = ctk.ctkCollapsibleButton() patientsCollapsibleButton.text = "Patients" #don't add it yet self.layout.addWidget(patientsCollapsibleButton) patientsFormLayout = qt.QFormLayout(patientsCollapsibleButton) self.patientList=qt.QComboBox() self.patientList.currentIndexChanged.connect(self.onPatientListChanged) self.patientList.setEditable(True) self.patientList.setInsertPolicy(qt.QComboBox.NoInsert) patientsFormLayout.addRow("Patient:",self.patientList) self.visitList=qt.QComboBox() self.visitList.currentIndexChanged.connect(self.onVisitListChanged) patientsFormLayout.addRow("Visit:",self.visitList) self.ctCode=qt.QLabel("ctCode") patientsFormLayout.addRow("CT:",self.ctCode) self.petCode=qt.QLabel("petCode") patientsFormLayout.addRow("PET:",self.petCode) self.patientLoad=qt.QPushButton("Load") self.patientLoad.clicked.connect(self.onPatientLoadButtonClicked) patientsFormLayout.addRow("Load patient",self.patientLoad) #no Save button #self.patientSave=qt.QPushButton("Save") #self.patientSave.clicked.connect(self.onPatientSaveButtonClicked) #patientsFormLayout.addRow("Save segmentation",self.patientSave) self.patientClear=qt.QPushButton("Clear") self.patientClear.clicked.connect(self.onPatientClearButtonClicked) patientsFormLayout.addRow("Clear patient",self.patientClear) self.keepCached=qt.QCheckBox("keep Cached") self.keepCached.setChecked(1) patientsFormLayout.addRow("Keep cached",self.keepCached) def addSetupSection(self): setupCollapsibleButton = ctk.ctkCollapsibleButton() setupCollapsibleButton.text = "Setup" self.layout.addWidget(setupCollapsibleButton) #Form layout (maybe one can think of more intuitive layouts) setupFormLayout = qt.QFormLayout(setupCollapsibleButton) self.serverList=qt.QComboBox() self.serverList.addItem('') self.setupList.addItem("limfomiPET_iBrowser.json") self.setupList.addItem("iraemm_iBrowserProspective.json") self.setupList.addItem("iraemm_iBrowserRetrospective.json") self.setupList.addItem("ORLPET_iBrowser.json") self.setupList.currentIndexChanged.connect(self.onSetupListChanged) setupFormLayout.addRow("Setup:",self.setupList) def addReviewSection(self): # # Review Area (not used, relic) # reviewCollapsibleButton = ctk.ctkCollapsibleButton() reviewCollapsibleButton.text = "Review" self.layout.addWidget(reviewCollapsibleButton) self.reviewBoxLayout = qt.QVBoxLayout(reviewCollapsibleButton) self.reviewFormLayout = qt.QFormLayout() self.reviewSegment=qt.QComboBox() self.reviewSegment.currentIndexChanged.connect(\ self.onReviewSegmentChanged) self.reviewFormLayout.addRow("Selected region:",self.reviewSegment) self.reviewResult=qt.QComboBox() sLabel="What do you think about the segmentation:" self.reviewFormLayout.addRow(sLabel,self.reviewResult) reviewOptions=['Select','Excellent','Minor deficiencies',\ 'Major deficiencies','Unusable'] for opt in reviewOptions: self.reviewResult.addItem(opt) self.aeResult=qt.QComboBox() aeLabel="Is organ suffering from adverse effect?" self.reviewFormLayout.addRow(aeLabel,self.aeResult) aeOptions=['Select','Yes','No'] for opt in aeOptions: self.aeResult.addItem(opt) #self.aeResult.setCurrentIndex(0) self.updateReview=qt.QPushButton("Save") saLabel="Save segmentation and AE decision for current segment" self.reviewFormLayout.addRow(saLabel,self.updateReview) self.updateReview.clicked.connect(self.onUpdateReviewButtonClicked) self.reviewBoxLayout.addLayout(self.reviewFormLayout) submitFrame=qt.QGroupBox("Submit data") self.submitFormLayout=qt.QFormLayout() self.reviewComment=qt.QTextEdit("this is a test") self.submitFormLayout.addRow("Comments (optional)",self.reviewComment) self.submitReviewButton=qt.QPushButton("Submit") self.submitFormLayout.addRow("Submit to database",\ self.submitReviewButton) self.submitReviewButton.clicked.connect(\ self.onSubmitReviewButtonClicked) submitFrame.setLayout(self.submitFormLayout) submitFrame.setFlat(1) #submitFrame.setFrameShape(qt.QFrame.StyledPanel) #submitFrame.setFrameShadow(qt.QFrame.Sunken) submitFrame.setStyleSheet("background-color:rgba(220,215,180,45)") self.reviewBoxLayout.addWidget(submitFrame) def addSegmentViewer(self): #not used (for review) editorCollapsibleButton = ctk.ctkCollapsibleButton() editorCollapsibleButton.text = "Segment Viewer" self.layout.addWidget(editorCollapsibleButton) self.vSegViewLayout=qt.QVBoxLayout(editorCollapsibleButton) segList=' '.join(self.logic.segmentList) label=qt.QLabel(" {}".format(segList)) self.vSegViewLayout.addWidget(label) self.segList=[] def addWindowManipulator(self): windowManipulatorCollapsibleButton=ctk.ctkCollapsibleButton() windowManipulatorCollapsibleButton.text="CT Window Manipulator" self.layout.addWidget(windowManipulatorCollapsibleButton) hLayout=qt.QHBoxLayout(windowManipulatorCollapsibleButton) ctWins={'CT:bone':self.onCtBoneButtonClicked, 'CT:air':self.onCtAirButtonClicked, 'CT:abdomen':self.onCtAbdomenButtonClicked, 'CT:brain':self.onCtBrainButtonClicked, 'CT:lung':self.onCtLungButtonClicked} for ctWin in ctWins: ctButton=qt.QPushButton(ctWin) ctButton.clicked.connect(ctWins[ctWin]) hLayout.addWidget(ctButton) def onSetupListChanged(self,i): status=self.logic.setConfig(self.setupList.currentText) try: if status['error']=='FILE NOT FOUND': print('File {} not found.'.format(self.setupList.currentText)) return except KeyError: pass #sort ids ids=status['ids'] ids.sort() self.updatePatientList(ids) self.onPatientListChanged(0) def onServerListChanged(self,i): status=self.logic.setServer(self.serverList.currentText) try: if status['error']=='KEY ERROR': self.serverList.setStyleSheet('background-color: violet') if status['error']=='ID ERROR': self.serverList.setStyleSheet('background-color: red') return except KeyError: pass self.idField.setText(status['id']) self.userField.setText(status['displayName']) self.serverList.setStyleSheet('background-color: green') def onPatientListChanged(self,i): self.visitList.clear() self.petCode.setText("") self.ctCode.setText("") #add potential filters from setup to dbFilter ds=self.logic.getDataset(dbFilter={'participant':self.patientList.currentText}) visitVar=self.logic.getVarName(var='visitField') dt=datetime.datetime #label is a combination of sequence number and date of imaging try: seq={row['SequenceNum']: {'label':row[visitVar], 'date': dt.strptime(row['studyDate'],'%Y/%m/%d %H:%M:%S')} for row in ds['rows']} except TypeError: #if studyDate is empty, this will return no possible visits return #apply lookup to visitVar if available try: seq={x: {'label':ds['lookups'][visitVar][seq[x]['label']], 'date':seq[x]['date']} for x in seq} except KeyError: pass #format label seq={x:'{} ({})'.format(seq[x]['label'],dt.strftime(seq[x]['date'],'%d-%b-%Y')) for x in seq} for s in seq: #onVisitListChanged is called for every addItem self.visitList.addItem(seq[s],s) #self.onVisitListChanged(0) def onVisitListChanged(self,i): #ignore calls on empty list if self.visitList.count==0: return #get sequence num s=self.visitList.itemData(i) print("Visit: SequenceNum:{}, label{}".format(s,self.visitList.currentText)) dbFilter={'participant':self.patientList.currentText, 'seqNum':s} ds=self.logic.getDataset(dbFilter=dbFilter) if not len(ds['rows'])==1: print("Found incorrect number {} of matches for [{}]/[{}]".\ format(len(ds['rows']),\ self.patientList.currentText,s)) row=ds['rows'][0] #copy row properties for data access self.currentRow=row self.petCode.setText(row[self.petField.text]) self.ctCode.setText(row[self.ctField.text]) #self.segmentationCode.setText(row[self.segmentationField.text]) def updatePatientList(self,ids): self.patientList.clear() for id in ids: 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.updateSegmentationViewer(self.logic.segmentationRows) #self.logic.loadReview(self.currentRow) #self.logic.loadAE(self.currentRow) #self.onReviewSegmentChanged() 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 def onPatientClearButtonClicked(self): self.logic.clearVolumesAndSegmentations() def onPatientSaveButtonClicked(self): #not used self.logic.saveSegmentation() def onCtBoneButtonClicked(self): self.logic.setWindow('CT:bone') def onCtAirButtonClicked(self): self.logic.setWindow('CT:air') def onCtAbdomenButtonClicked(self): self.logic.setWindow('CT:abdomen') def onCtBrainButtonClicked(self): self.logic.setWindow('CT:brain') 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 def loadLibrary(name): #utility function to load nix library from git fwrapper=nixModule.getWrapper('nixWrapper.py') p=pathlib.Path(fwrapper) sys.path.append(str(p.parent)) import nixWrapper return nixWrapper.loadLibrary(name) # # segmentationBrowserLogic # class segmentationBrowserLogic(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=None): ScriptedLoadableModuleLogic.__init__(self, parent) print('segmentationBrowserLogic loading') if not parent==None: #use layout and data from parent widget self.parent=parent fhome=os.path.expanduser('~') fsetup=os.path.join(fhome,'.labkey','setup.json') try: with open(fsetup) as f: self.setup=json.load(f) except FileNotFoundError: self.setup={} try: pt=self.setup['paths'] except KeyError: self.setup['paths']={} lName='labkeyInterface' loadLibrary(lName) import labkeyInterface import labkeyDatabaseBrowser import labkeyFileBrowser self.network=labkeyInterface.labkeyInterface() self.dbBrowser=labkeyDatabaseBrowser self.fBrowser=labkeyFileBrowser self.tempDir=os.path.join(os.path.expanduser('~'),'temp') if not os.path.isdir(self.tempDir): os.mkdir(self.tempDir) self.lookups={} self.segmentList=["Liver","Blood","Marrow","Disease","Deauville"] print('segmentationBrowserLogic setup complete') def setServer(self,serverName): #additional way of setting the labkey network interface #if no parent was provided in logic initialization (stand-alone mode) status={} fileName="NONE" if serverName=="astuden": fileName="astuden.json" if serverName=="adoma": fileName="adoma.json" if serverName=="kzevnik": fileName="kzevnik.json" if fileName=="NONE": print("No path was associated with server {}".format(serverName)) status['error']='KEY ERROR' return status fconfig=os.path.join(os.path.expanduser('~'),'.labkey',fileName) self.network.init(fconfig) self.remoteId=self.network.getUserId() if self.remoteId==None: status['error']='ID ERROR' return status status['displayName']=self.remoteId['displayName'] status['id']=self.remoteId['id'] #reset db and fb (they are thin classes anyhow) self.db=self.dbBrowser.labkeyDB(self.network) self.fb=self.fBrowser.labkeyFileBrowser(self.network) return status def setConfig(self,configName): status={} fileName=os.path.join(os.path.expanduser('~'),'.labkey',configName) if not os.path.isfile(fileName): status['error']='FILE NOT FOUND' return status with open(fileName,'r') as f: self.isetup=json.load(f) #self.project=self.isetup['project'] #"iPNUMMretro/Study" #self.schema='study' #self.dataset=self.isetup['query'] #include filters... ds=self.getDataset() ids=[row[self.isetup['participantField']] for row in ds['rows']] status['ids']=list(set(ids)) return status def getVarName(self,name="Imaging",var="visitField"): dset=self.isetup['datasets'][name] defaults={"visitField":"imagingVisitId"} try: return dset[var] except KeyError: return defaults[var] def getDataset(self,name="Imaging",dbFilter={}): dset=self.isetup['datasets'][name] project=dset['project'] schema=dset['schema'] query=dset['query'] #add default filters qFilter=[] try: for qf in dset['filter']: v=dset['filter'][qf] qFilter.append({'variable':qf,'value':v,'oper':'eq'}) except KeyError: pass for f in dbFilter: if f=='participant': qFilter.append({'variable':self.isetup['participantField'], 'value':dbFilter[f],'oper':'eq'}) continue if f=='seqNum': qFilter.append({'variable':'SequenceNum', 'value':'{}'.format(dbFilter[f]), 'oper':'eq'}) continue qFilter.append({'variable':f,'value':dbFilter[f],'oper':'eq'}) try: ds=self.db.selectRows(project,schema,query, \ qFilter,dset['view']) except KeyError: ds=self.db.selectRows(project,schema,query,qFilter) #get lookups as well lookups={} for f in ds['metaData']['fields']: try: lookup=f['lookup'] except KeyError: continue var=f['name'] lookupCode='{}:{}'.format(lookup['schema'],lookup['queryName']) try: lookups[var]=self.lookups[lookupCode] except KeyError: self.lookups[lookupCode]=self.loadLookup(project,lookup) lookups[var]=self.lookups[lookupCode] return {'rows':ds['rows'],'lookups':lookups} def loadLookup(self,project,lookup): qData={} key=lookup['keyColumn'] value=lookup['displayColumn'] fSet=self.db.selectRows(project,lookup['schema'],lookup['queryName'],[]) for q in fSet['rows']: qData[q[key]]=q[value] return qData def loadImage(self,iData): #unpack iData idx=iData['idx'] path=iData['path'] keepCached=iData['keepCached'] dset=self.isetup['datasets'][iData['dataset']] localPath=os.path.join(self.tempDir,path[-1]) if not os.path.isfile(localPath): #download from server remotePath=self.fb.formatPathURL(dset['project'],'/'.join(path)) if not self.fb.entryExists(remotePath): print("Failed to get {}".format(remotePath)) return self.fb.readFileToFile(remotePath,localPath) properties={} filetype='VolumeFile' #make sure segmentation gets loaded as a labelmap if idx=="Segmentation": filetype='SegmentationFile' #properties["labelmap"]=1 try: volumeIdx=iData['volumeIdx'] except KeyError: volumeIdx=idx self.volumeNode[volumeIdx]=slicer.util.loadNodeFromFile(localPath, filetype=filetype,properties=properties) if not keepCached: os.remove(localPath) def loadImages(self,row,keepCached): #fields={'ctResampled':True,'petResampled':False} fields={"CT":self.parent.ctField.text,\ "PET":self.parent.petField.text} path=[self.isetup['imageDir'],row['patientCode'],row['visitCode']] relativePaths={x:path+[row[y]] for (x,y) in fields.items()} self.volumeNode={} for f in relativePaths: iData={'idx':f,'path':relativePaths[f], 'keepCached':keepCached,'dataset':'Imaging'} self.loadImage(iData) #mimic abdominalCT standardized window setting self.volumeNode['CT'].GetScalarVolumeDisplayNode().\ SetAutoWindowLevel(False) self.volumeNode['CT'].GetScalarVolumeDisplayNode().\ SetWindowLevel(1400, -500) #set colormap for PET to PET-Heat (this is a verbatim setting from #the Volumes->Display->Lookup Table colormap identifier) self.volumeNode['PET'].GetScalarVolumeDisplayNode().\ SetAndObserveColorNodeID(\ slicer.util.getNode('Inferno').GetID()) slicer.util.setSliceViewerLayers(background=self.volumeNode['CT'],\ foreground=self.volumeNode['PET'],foregroundOpacity=0.5,fit=True) def loadSegmentations(self,row, loadFile=1): dbFilter={ #'User':'{}'.format(self.remoteId['id']), 'participant':row[self.isetup['participantField']], 'visitCode':row['visitCode']} ds=self.getDataset(name='SegmentationsMaster', dbFilter=dbFilter) self.segmentationRows=ds['rows'] if len(self.segmentationRows)<0: print('No segmentations for {}'.format(dbFilter)) return for r in self.segmentationRows: #update self.segmentationEntry #self.segmentationEntry=ds['rows'][0] #self.segmentationEntry['origin']='database' #if loadFile: self.loadSegmentationFromEntry(r) #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,entry): path=[self.isetup['imageDir'], entry['patientCode'], entry['visitCode']] path.append('Segmentations') return path def loadSegmentationFromEntry(self,r): #compile path entry=r path=self.getSegmentationPath(entry) path.append(entry['latestFile']) iData={'idx':'Segmentation','path':path, 'keepCached':0,'dataset':'SegmentationsMaster', 'volumeIdx':entry['latestFile']} self.loadImage(iData) #look for missing segments return #skip this segNode=self.volumeNode['Segmentation'] seg=segNode.GetSegmentation() segNames=[seg.GetNthSegmentID(i) for i in range(seg.GetNumberOfSegments())] print('Segments') try: segmentList=self.isetup['segmentList'] except KeyError: segmentList=self.segmentList for s in segmentList: if s not in segNames: seg.AddEmptySegment(s,s) 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) def saveSegmentation(self): #not used #get the latest key by adding an entry to SegmentationList copyFields=[self.isetup['participantField'],'patientCode','visitCode','User'] outRow={x:self.segmentationEntry[x] for x in copyFields} sList=self.isetup['datasets']['SegmentationsList'] resp=self.db.modifyRows('insert',sList['project'], sList['schema'],sList['query'],[outRow]) encoding=chardet.detect(resp)['encoding'] respJSON=json.loads(resp.decode(encoding)) outRow=respJSON['rows'][0] #print(outRow) #construct file name with known key uName=re.sub(' ','_',self.remoteId['displayName']) fName='Segmentation_{}-{}_{}_{}.nrrd'.format( self.segmentationEntry['patientCode'], self.segmentationEntry['visitCode'], uName,outRow['Key']) path=self.getSegmentationPath() path.append(fName) self.saveNode(self.volumeNode['Segmentation'],'SegmentationsMaster',path) #update SegmentationList with know file name outRow['Segmentation']=fName self.db.modifyRows('update',sList['project'], sList['schema'],sList['query'],[outRow]) #update SegmentationsMaster self.segmentationEntry['latestFile']=fName self.segmentationEntry['version']=outRow['Key'] des=self.isetup['datasets']['SegmentationsMaster'] op='insert' if self.segmentationEntry['origin']=='database': op='update' print('Saving: mode={}'.format(op)) resp=self.db.modifyRows(op,des['project'], des['schema'],des['query'],[self.segmentationEntry]) print(resp) #since we loaded a version, origin should be set to database self.loadSegmentation(self.segmentationEntry,0) #self.segmentationEntry['origin']='database' def saveNode(self,node,dataset,path): #not used fName=path[-1] localPath=os.path.join(self.tempDir,fName) slicer.util.saveNode(node,localPath) dset=self.isetup['datasets'][dataset] #exclude file name when building directory structure remotePath=self.fb.buildPathURL(dset['project'],path[:-1]) remotePath+='/'+fName self.fb.writeFileToFile(localPath,remotePath) #add entry to segmentation list def createSegmentation(self,entry): #not used #create segmentation entry for database update #note that origin is not set to database copyFields=[self.isetup['participantField'],'patientCode','visitCode','SequenceNum'] #copyFields=['ParticipantId','patientCode','visitCode','SequenceNum'] self.segmentationEntry={x:entry[x] for x in copyFields} self.segmentationEntry['User']=self.remoteId['id'] self.segmentationEntry['origin']='created' self.segmentationEntry['version']=-1111 #create a segmentation node uName=re.sub(' ','_',self.remoteId['displayName']) segNode=slicer.vtkMRMLSegmentationNode() self.volumeNode['Segmentation']=segNode segNode.SetName('Segmentation_{}_{}_{}'. format(entry['patientCode'],entry['visitCode'],uName)) slicer.mrmlScene.AddNode(segNode) segNode.CreateDefaultDisplayNodes() segNode.SetReferenceImageGeometryParameterFromVolumeNode(self.volumeNode['PET']) try: segmentList=self.isetup['segmentList'] except KeyError: segmentList=self.segmentList for s in segmentList: segNode.GetSegmentation().AddEmptySegment(s,s) def clearVolumesAndSegmentations(self): 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={} def setWindow(self,windowName): #default w=1400 l=-500 if windowName=='CT:bone': w=1000 l=400 if windowName=='CT:air': w=1000 l=-426 if windowName=='CT:abdomen': w=350 l=40 if windowName=='CT:brain': w=100 l=50 if windowName=='CT:lung': w=1400 l=-500 self.volumeNode['CT'].GetScalarVolumeDisplayNode().\ SetWindowLevel(w,l) print('setWindow: {} {}/{}'.format(windowName,w,l)) class segmentationBrowserTest(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_irAEMMBrowser() def test_irAEMMBrowser(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 #