Andrej Studen преди 7 години
родител
ревизия
d8a72d0f94
променени са 2 файла, в които са добавени 190 реда и са изтрити 76 реда
  1. 87 72
      labkeySlicerPythonExtension/labkeySlicerPythonExtension.py
  2. 103 4
      labkeySlicerPythonExtension/slicerNetwork.py

+ 87 - 72
labkeySlicerPythonExtension/labkeySlicerPythonExtension.py

@@ -39,7 +39,7 @@ class labkeySlicerPythonExtensionWidget(ScriptedLoadableModuleWidget):
     ScriptedLoadableModuleWidget.setup(self)
     # Instantiate and connect widgets ...
     self.logic=labkeySlicerPythonExtensionLogic(self)
-    self.network=slicerNetwork.slicerNetwork(self)
+    self.network=slicerNetwork.labkeyURIHandler()
     #
     # Parameters Area
     #
@@ -72,85 +72,64 @@ class labkeySlicerPythonExtensionWidget(ScriptedLoadableModuleWidget):
 
     connectionFormLayout.addRow("CA certificate:",self.caCertButton)
 
+    self.auth_pwd=''
     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)
+    fileDialogCollapsibleButton = ctk.ctkCollapsibleButton()
+    fileDialogCollapsibleButton.text = "Remote files"
+    self.layout.addWidget(fileDialogCollapsibleButton)
 
     # Layout within the dummy collapsible button
-    parametersFormLayout = qt.QFormLayout(parametersCollapsibleButton)
+    fileDialogFormLayout = qt.QFormLayout(fileDialogCollapsibleButton)
 
-    #
-    # 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)
+    #add item list for each found file/directory
+    self.fileList=qt.QListWidget(parent=self)
+    self.fileList.toolTip="Select remote file"
+    self.fileList.currentItemChanged.connect(self.onFileListItemChanged)
+    self.currentRemoteDir=''
 
-    #
-    # 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)
+    #add dummy entry
+    items=('.','..')
+    self.populateFileList(items)
 
-    #
-    # 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)
+    fileDialogFormLayout.addWidget(self.fileList)
 
-    #
-    # 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)
+    #add selected file display
+    self.selectedFile=qt.QLineEdit(parent=self)
+    self.selectedFile.toolTip="Selected file"
 
-    #
-    # Apply Button
-    #
-    self.applyButton = qt.QPushButton("Apply")
-    self.applyButton.toolTip = "Run the algorithm."
-    self.applyButton.enabled = False
-    parametersFormLayout.addRow(self.applyButton)
+    fileDialogFormLayout.addRow("Selected file :",self.selectedFile)
 
-    # connections
-    self.applyButton.connect('clicked(bool)', self.onApplyButton)
-    self.inputSelector.connect("currentNodeChanged(vtkMRMLNode*)", self.onSelect)
-    self.outputSelector.connect("currentNodeChanged(vtkMRMLNode*)", self.onSelect)
+    #add possible file Content
+    self.fileTypeSelector=qt.QComboBox()
+    self.fileTypeSelector.toolTip="Select file type"
 
-    # Add vertical spacer
-    self.layout.addStretch(1)
+    items=self.network.fileTypesAvailable()
+    self.populateFileTypeSelector(items)
+
+    fileDialogFormLayout.addRow("File type :",self.fileTypeSelector)
+
+    loadFileButton=qt.QPushButton("Load file")
+    loadFileButton.toolTip="Load file"
+    loadFileButton.clicked.connect(self.onLoadFileButtonClicked)
+
+    fileDialogFormLayout.addRow("Action :",loadFileButton)
+
+
+  def populateFileList(self,items):
+      self.fileList.clear()
+      for it_text in items:
+            item=qt.QListWidgetItem(self.fileList)
+            item.setText(it_text)
+            item.setIcon(qt.QIcon(it_text))
+
+  def populateFileTypeSelector(self,items):
+      for item in items:
+          self.fileTypeSelector.addItem(item)
 
   def cleanup(self):
     pass
@@ -239,13 +218,49 @@ class labkeySlicerPythonExtensionWidget(ScriptedLoadableModuleWidget):
       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.network.connectRemote(str(self.serverURL.text),uname,pwd)
-
-
-
+      self.auth_pwd=qt.QInputDialog.getText(None,
+        "Labkey credentials","Enter password",qt.QLineEdit.Password,self.auth_pwd)
+
+      self.network.connectRemote(str(self.serverURL.text),uname,self.auth_pwd)
+
+  def onFileListItemChanged(self,curr,prev):
+        pText="None"
+        if prev != None:
+            pText=prev.text()
+        if curr == None:
+            print "Selected items: {0} -> None".format(pText)
+            return
+
+        print "Selected items: {0} -> None".format(pText)
+
+
+        #this is hard -> compose path string from currentRemoteDir and selection
+        if curr.text().find('..')==0:
+            #one up
+            idx=self.currentRemoteDir.rfind('/')
+            if idx<0:
+                self.currentRemoteDir=''
+            else:
+                self.currentRemoteDir=self.currentRemoteDir[:idx]
+        elif curr.text().find('.')==0:
+            pass
+        else:
+            if len(self.currentRemoteDir)>0:
+                self.currentRemoteDir+='/'
+            self.currentRemoteDir+=curr.text()
+        print "Listing {0}".format(self.currentRemoteDir)
+        flist=self.network.toRelativePath(
+            self.network.listDir(self.currentRemoteDir))
+        print "Got"
+        print flist
+        flist.insert(0,'..')
+        flist.insert(0,'.')
+        self.populateFileList(flist)
+        self.selectedFile.setText(self.currentRemoteDir)
+
+  def onLoadFileButtonClicked(self):
+      self.network.loadNodeFromFile(self.selectedFile.text,
+            self.fileTypeSelector.currentText)
 
 #
 # labkeySlicerPythonExtensionLogic

+ 103 - 4
labkeySlicerPythonExtension/slicerNetwork.py

@@ -5,12 +5,100 @@ import cookielib
 import xml.etree.ElementTree as ET
 import re
 import StringIO
+import slicer
+import shutil
+import distutils
+import os
 
 class slicerNetwork:
     def __init__(self,parent):
         parent.title = "slicerNetwork"
         self.parent=parent
 
+class labkeyURIHandler(slicer.vtkURIHandler):
+    def __init__(self):
+        slicer.vtkURIHandler.__init__(self)
+        self.className="labkeyURIHandler"
+        slicer.mrmlScene.AddURIHandler(self)
+        self.localCacheDirectory=os.path.join(os.environ["HOME"],"labkeyCache")
+
+    def CanHandleURI(self,uri):
+        print "labkeyURIHandler::CanHandleURI({0})".format(uri)
+        if uri.find('labkey://')==0:
+            return 1
+        return 0
+
+    def GetClassName(self):
+        return self.className
+
+    def GetHostName(self):
+        return self.hostname
+
+    def SetHostName(self,host):
+        self.hostname=host
+
+    def SetLocalCahceDirectory(self,dir):
+        self.localCacheDirectory=dir
+
+    def GetLocalCacheDirectory(self):
+        return self.localCacheDirectory
+
+    def GetLocalPath(self,source):
+        relativePath=re.sub('labkey://','',source)
+        relativePath=re.sub('/',os.sep,relativePath)
+        return os.path.join(self.localCacheDirectory,relativePath)
+
+    def GetFile(self,source):
+        # check for file in cache. If available, use, if not, download
+        localPath=self.GetLocalPath(source)
+        if os.path.isfile(localPath):
+            return localPath
+        self.StageFileRead(source,localPath)
+        return localPath
+
+    def StageFileRead(self,source,dest):
+        print "labkeyURIHandler::StageFileRead({0},{1})".format(
+            source,dest)
+        labkeyPath=re.sub('labkey://','',source)
+        remote=self.readFile(self.hostname,labkeyPath)
+        #make all necessary directories
+        path=os.path.dirname(dest)
+        try:
+            os.makedirs(path)
+        except OSError:
+            if not os.path.isdir(path):
+                raise
+        local=open(dest,'w')
+        #make sure we are at the begining of the file
+        remote.seek(0)
+        shutil.copyfileobj(remote,local)
+        local.close()
+
+    def StageFileWrite(self,source,dest):
+        print "labkeyURIHandler::StageFileWrite({0},{1}) not implemented yet".format(
+            source,dest)
+
+    def fileTypesAvailable(self):
+        return ('VolumeFile','SegmentationFile','TransformFile')
+
+    #mimic slicer.util.loadNodeFromFile
+    def loadNodeFromFile(self, filename, filetype, properties={}, returnNode=False):
+        #this is the only relevant part - file must be downloaded to cache
+        localPath=self.GetFile(filename)
+        slicer.util.loadNodeFromFile(localPath,filetype,properties,returnNode)
+
+    def loadVolume(self,filename, properties={}, returnNode=False):
+        filetype = 'VolumeFile'
+        #redirect to self.loadNodeFromFile first to get the cached file
+        return self.loadNodeFromFile(filename,filetype, properties,returnNode)
+
+    def loadSegmentation(self,filename,properties={},returnNode=False):
+        filetype='SegmentationFile'
+        #redirect to self.loadNodeFromFile first to get the cached file
+        return self.loadNodeFromFile(filename,filetype, properties,returnNode)
+    #add others if needed
+
+
     def configureSSL(self,cert,key,pwd,cacert):
         #do this first
         self.ctx=ssl.SSLContext(ssl.PROTOCOL_SSLv23)
@@ -20,7 +108,7 @@ class slicerNetwork:
 
     def connectRemote(self,serverUrl,uname,pwd):
         https_handler=urllib2.HTTPSHandler(context=self.ctx)
-
+        self.SetHostName(serverUrl)
         #cookie part
         cj=cookielib.CookieJar()
         cookie_handler=urllib2.HTTPCookieProcessor(cj)
@@ -54,8 +142,9 @@ class slicerNetwork:
         f=self.opener.open(r)
         #f contains json as a return value
 
-    def listDir(self,serverUrl,dir):
-        dirUrl=serverUrl+"/labkey/_webdav"+"/"+dir
+    def listDir(self,dir):
+        print "Listing for {0}".format(dir)
+        dirUrl=self.hostname+"/labkey/_webdav"+"/"+dir
         r=propfindRequest(dirUrl)
         PROPFIND=u"""<?xml version="1.0" encoding="utf-8"?>\n
                     <propfind xmlns="DAV:">\n
@@ -79,11 +168,21 @@ class slicerNetwork:
         del dirs[0]
         return dirs
 
+    def toRelativePath(self,dirs):
+        flist1=[]
+        for d in dirs:
+            if d[-1]=='/':
+                d=d[:-1]
+            if d.rfind('/')>-1:
+                d=d[d.rfind('/')+1:]
+            flist1.append(d)
+        return flist1
+
     def readFile(self, serverUrl, path):
         dirUrl=serverUrl+"/labkey/_webdav"+"/"+path
         f=self.get(dirUrl)
         return StringIO.StringIO(f.read())
-        
+
 class propfindRequest(urllib2.Request):
     """
     This request subclass allows explicit specification of