fileIO.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. import slicerNetwork
  2. import slicer
  3. import qt
  4. import ctk
  5. import json
  6. import os
  7. import shutil
  8. class fileIO(slicer.ScriptedLoadableModule.ScriptedLoadableModule):
  9. def __init__(self,parent):
  10. slicer.ScriptedLoadableModule.ScriptedLoadableModule.__init__(self, parent)
  11. self.className="fileIO"
  12. self.parent.title="fileIO"
  13. self.parent.categories = ["LabKey"]
  14. self.parent.dependencies = []
  15. self.parent.contributors = ["Andrej Studen (UL/FMF)"] # replace with "Firstname Lastname (Organization)"
  16. self.parent.helpText = """
  17. File IO for Labkey interface to slicer
  18. """
  19. self.parent.acknowledgementText = """
  20. Developed within the medical physics research programme of the Slovenian research agency.
  21. """ # replace with organization, grant and thanks.
  22. class remoteFileSelector(qt.QMainWindow):
  23. def __init__(self, parent=None):
  24. super(remoteFileSelector, self).__init__(parent)
  25. self.setup()
  26. def setMaster(self,master):
  27. self.master=master
  28. def setup(self):
  29. centralWidget=qt.QWidget(self)
  30. fileDialogFormLayout = qt.QFormLayout(centralWidget)
  31. #self.layout.addWidget(fileDialogFormLayout)
  32. #add item list for each found file/directory
  33. self.fileList=qt.QListWidget()
  34. self.fileList.toolTip="Select remote file"
  35. self.fileList.itemDoubleClicked.connect(self.onFileListDoubleClicked)
  36. self.currentRelativeDir=''
  37. #add dummy entry
  38. items=('.','..')
  39. self.populateFileList(items)
  40. fileDialogFormLayout.addWidget(self.fileList)
  41. self.selectedPath=qt.QLineEdit("")
  42. self.selectedPath.toolTip="Selected path"
  43. fileDialogFormLayout.addRow("Selected path :",self.selectedPath)
  44. self.closeButton=qt.QPushButton("Close")
  45. self.closeButton.toolTip="Close"
  46. self.closeButton.connect('clicked(bool)',self.onCloseButtonClicked)
  47. fileDialogFormLayout.addRow(self.closeButton)
  48. self.setCentralWidget(centralWidget);
  49. def onFileListDoubleClicked(self,item):
  50. if item == None:
  51. print("Selected items: None")
  52. return
  53. iText=item.text()
  54. print ("Selected items: {0} ").format(iText)
  55. #this is hard -> compose path string from currentRelativeDir and selection
  56. if item.text().find('..')==0:
  57. #one up
  58. idx=self.currentRelativeDir.rfind('/')
  59. if idx<0:
  60. self.currentRelativeDir=''
  61. else:
  62. self.currentRelativeDir=self.currentRelativeDir[:idx]
  63. elif item.text().find('.')==0:
  64. pass
  65. else:
  66. if len(self.currentRelativeDir)>0:
  67. self.currentRelativeDir+='/'
  68. self.currentRelativeDir+=item.text()
  69. print ("Listing {0}").format(self.currentRelativeDir)
  70. flist=self.master.network.toRelativePath(
  71. self.master.network.listRelativeDir(self.currentRelativeDir))
  72. print ("Got")
  73. print (flist)
  74. flist.insert(0,'..')
  75. flist.insert(0,'.')
  76. self.populateFileList(flist)
  77. self.selectedPath.setText(self.currentRelativeDir)
  78. self.master.remotePath.setText(self.master.network.GetLabkeyPathFromRelativePath(self.currentRelativeDir))
  79. def populateFileList(self,items):
  80. self.fileList.clear()
  81. for it_text in items:
  82. item=qt.QListWidgetItem(self.fileList)
  83. item.setText(it_text)
  84. item.setIcon(qt.QIcon(it_text))
  85. def onCloseButtonClicked(self):
  86. self.hide()
  87. class fileIOWidget(slicer.ScriptedLoadableModule.ScriptedLoadableModuleWidget):
  88. def __init__(self,parent):
  89. slicer.ScriptedLoadableModule.ScriptedLoadableModuleWidget.__init__(self, parent)
  90. self.selectRemote=remoteFileSelector()
  91. #self.selectLocal=qt.QFileDialog()
  92. #self.selectLocal.connect('fileSelected(QString)',self.onLocalFileSelected)
  93. self.selectRemote.setMaster(self)
  94. self.network=slicerNetwork.labkeyURIHandler()
  95. def setup(self):
  96. connectionCollapsibleButton = ctk.ctkCollapsibleButton()
  97. connectionCollapsibleButton.text = "Connection"
  98. self.layout.addWidget(connectionCollapsibleButton)
  99. connectionFormLayout = qt.QFormLayout(connectionCollapsibleButton)
  100. self.loadConfigButton=qt.QPushButton("Load configuration")
  101. self.loadConfigButton.toolTip="Load configuration"
  102. self.loadConfigButton.connect('clicked(bool)',self.onLoadConfigButtonClicked)
  103. connectionFormLayout.addRow("Connection:",self.loadConfigButton)
  104. fileDialogCollapsibleButton = ctk.ctkCollapsibleButton()
  105. fileDialogCollapsibleButton.text = "Paths"
  106. self.layout.addWidget(fileDialogCollapsibleButton)
  107. fileDialogFormLayout = qt.QFormLayout(fileDialogCollapsibleButton)
  108. self.listRemoteButton=qt.QPushButton("List Remote")
  109. self.listRemoteButton.toolTip="List Remote"
  110. self.listRemoteButton.connect('clicked(bool)',self.onListRemoteButtonClicked)
  111. fileDialogFormLayout.addRow(self.listRemoteButton)
  112. self.listLocalButton=qt.QPushButton("List Local")
  113. self.listLocalButton.toolTip="List Local"
  114. self.listLocalButton.connect('clicked(bool)',self.onListLocalButtonClicked)
  115. fileDialogFormLayout.addRow(self.listLocalButton)
  116. #add selected file display
  117. self.remotePath=qt.QLineEdit("")
  118. self.remotePath.toolTip="Remote path"
  119. fileDialogFormLayout.addRow("RemotePath :",self.remotePath)
  120. self.localPath=qt.QLineEdit("")
  121. self.localPath.toolTip="Local path"
  122. fileDialogFormLayout.addRow("LocalPath :",self.localPath)
  123. copyToRemoteButton=qt.QPushButton("Local->Remote")
  124. copyToRemoteButton.toolTip="Local -> Remote"
  125. copyToRemoteButton.connect('clicked(bool)',self.onCopyToRemoteButtonClicked)
  126. fileDialogFormLayout.addRow(copyToRemoteButton)
  127. copyToLocalButton=qt.QPushButton("Remote -> Local")
  128. copyToLocalButton.toolTip="Remote -> Local"
  129. copyToLocalButton.connect('clicked(bool)',self.onCopyToLocalButtonClicked)
  130. fileDialogFormLayout.addRow(copyToLocalButton)
  131. removeRemoteButton=qt.QPushButton("Remove remote")
  132. removeRemoteButton.toolTip="Remove Remote"
  133. removeRemoteButton.connect('clicked(bool)',self.onRemoveRemoteButtonClicked)
  134. fileDialogFormLayout.addRow(removeRemoteButton)
  135. mkdirRemoteButton=qt.QPushButton("Mkdir remote")
  136. mkdirRemoteButton.toolTip="Mkdir Remote"
  137. mkdirRemoteButton.connect('clicked(bool)',self.onMkdirRemoteButtonClicked)
  138. fileDialogFormLayout.addRow(mkdirRemoteButton)
  139. def onListLocalButtonClicked(self):
  140. self.localPath.setText(qt.QFileDialog.getOpenFileName(None,"Select local"))
  141. def onListRemoteButtonClicked(self):
  142. self.selectRemote.show()
  143. def onLoadConfigButtonClicked(self):
  144. filename=qt.QFileDialog.getOpenFileName(None,'Open configuration file (JSON)',
  145. '.', '*.json')
  146. with open(filename,'r') as f:
  147. dt=json.load(f)
  148. if dt.has_key('host'):
  149. self.network.hostname=dt['host']
  150. if dt.has_key('SSL'):
  151. self.network.configureSSL(dt['SSL']['user'],dt['SSL']['key'],
  152. dt['SSL']['keyPwd'],dt['SSL']['ca'])
  153. if dt.has_key('labkey'):
  154. if dt['labkey'].has_key('user'):
  155. self.network.auth_name=dt['labkey']['user']
  156. if dt['labkey'].has_key('password'):
  157. self.network.auth_pass=dt['labkey']['password']
  158. self.loadConfigButton.setText(os.path.basename(filename))
  159. self.network.initRemote()
  160. def copyLocalToRemote(self,localPath,remotePath):
  161. if os.path.isfile(localPath):
  162. #end recursion
  163. print ("Copy {} to {}").format(localPath,remotePath)
  164. self.network.copyLocalFileToRemote(localPath,remotePath)
  165. return
  166. dirName=os.path.basename(os.path.normpath(localPath))
  167. remotePath+='/'+dirName
  168. if not self.network.isRemoteDir(remotePath):
  169. self.network.mkdir(remotePath+'/')
  170. for f in os.listdir(localPath):
  171. #enter recursion
  172. localfile=os.path.join(localPath,f)
  173. self.copyLocalFileToRemote(localfile,remotePath)
  174. def copyRemoteToLocal(self,localPath,remotePath):
  175. ok,files=self.network.listRemoteDir(remotePath)
  176. if not ok:
  177. print('Troubles getting remote dir content for {}'.\
  178. format(remotePath))
  179. return
  180. #remove trailing slash
  181. for f in files:
  182. if f[-1]=='/':
  183. f=f[:-1]
  184. bf=f[f.rfind('/')+1:]
  185. dirUrl=self.network.GetLabkeyWebdavUrl()+"/"+f
  186. if self.network.isDir(dirUrl):
  187. lp=os.path.join(localPath,bf)
  188. if not os.path.isdir(lp):
  189. os.mkdir(lp)
  190. print('Creating {}'.format(lp))
  191. print('Copying directory {} to {}'.format(f,lp))
  192. self.copyRemoteToLocal(lp,f)
  193. else:
  194. rf=self.network.readFile(f)
  195. fout=os.path.join(localPath,bf)
  196. print('Copying file {} to {}'.format(f,fout))
  197. with open (fout, 'w') as fd:
  198. rf.seek (0)
  199. shutil.copyfileobj (rf, fd)
  200. fd.close()
  201. def onCopyToRemoteButtonClicked(self):
  202. self.copyLocalFileToRemote(self.localPath.text,self.remotePath.text)
  203. def onCopyToLocalButtonClicked(self):
  204. self.copyRemoteToLocal(self.localPath.text,self.remotePath.text)
  205. def onRemoveRemoteButtonClicked(self):
  206. remotePath=self.remotePath.text
  207. if self.network.isRemoteDir(remotePath):
  208. resp=qt.QMessageBox.question(None,'Delete directory',
  209. 'Do you want to delete directory {}'.format(remotePath))
  210. if resp == qt.QMessageBox.No:
  211. return
  212. self.network.rmdir(remotePath)
  213. def onMkdirRemoteButtonClicked(self):
  214. remotePath=self.remotePath.text
  215. self.network.mkdir(remotePath)
  216. class fileIOLogic(slicer.ScriptedLoadableModule.ScriptedLoadableModuleLogic):
  217. def __init__(self,parent):
  218. slicer.ScriptedLoadableModule.ScriptedLoadableModuleLogic.__init__(self, parent)
  219. class fileIOTest(slicer.ScriptedLoadableModule.ScriptedLoadableModuleTest):
  220. """
  221. This is the test case for your scripted module.
  222. Uses ScriptedLoadableModuleTest base class, available at:
  223. https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py
  224. """
  225. def setUp(self):
  226. """ Do whatever is needed to reset the state - typically a scene clear will be enough.
  227. """
  228. slicer.mrmlScene.Clear(0)
  229. def runTest(self):
  230. """Run as few or as many tests as needed here.
  231. """
  232. self.setUp()
  233. self.test_fileIO()
  234. def test_fileIO(self):
  235. """ Ideally you should have several levels of tests. At the lowest level
  236. tests should exercise the functionality of the logic with different inputs
  237. (both valid andclass dataExplorerTest(ScriptedLoadableModuleTest):
  238. """
  239. pass