Package client :: Module RemoteFile
[hide private]
[frames] | no frames]

Source Code for Module client.RemoteFile

  1  from PyQt4.QtGui  import * 
  2  from PyQt4.QtCore import * 
  3   
  4  import re 
  5  import Constants 
  6   
  7  # This class, when created, will go through everything until execution. 
  8  # The user will see a window of files and folders, and choose a file eventually. 
  9  # After the user accepts or rejects this dialog, the final result, if any, is stored 
 10  # is self.finalresult, which may be accessed.  Note that this class merely chooses a 
 11  # file from a remote location, it does nothing with that file name. 
 12   
 13  # The way it accomplishes this is through the basin_remote on the host side. 
 14  # A function was written to reveal the directory it keeps scripts that this client 
 15  # relies on.  Those scripts were improted, and kept the name `filesystem`, which 
 16  # has three functions: 
 17  #       filesystem.home ():  
 18  #               returns the remote user's home 
 19  #       filesystem.lsDir (path = home ()): 
 20  #               Given the path, or default home, it will display all 
 21  #               directories in the path 
 22  #       filesystem.lsFile (path = home ()): 
 23  #               Given the path, or default home, it will display all 
 24  #               files in the path 
25 -class RemoteFileBrowser (QDialog):
26 - def __init__ (self, bridge):
27 # Setup the dialog to be half the size of the main window, and centered over it 28 QDialog.__init__ (self, bridge._mainWindow) 29 self._bridge = bridge 30 mainRect = self._bridge._mainWindow.geometry () 31 width = mainRect.width () 32 height = mainRect.height () 33 x = mainRect.x () + int (width * 0.175) 34 y = mainRect.y () + int (height * 0.25) 35 width = int (width * 0.65) 36 height = int (height * 0.5) 37 self.setGeometry (x, y, width, height) 38 39 # The remote file browser bases its remote-ness off of a script that it finds 40 # on the remote server. The script is imported to the controller, not the 41 # cluster, thus everything MUST be done locally 42 self._bridge.makeLocal () 43 44 # Where the directory and file items will be held for each path. 45 # They are stored because the user may wish to change the file types 46 # they browse, and I would rather store the total ls result than 47 # continually probe the controller. 48 self.dirs = [] 49 self.files = [] 50 51 # will be accessed after the dialog is executed, for the file chosen 52 # it will contain the full file path and file name 53 self.finalresult = [] 54 55 # This is just dialog box stuff, a lot of it has been extracted into 56 # separate functions for the beauty of the code, but for no great purpose 57 self.layout = QVBoxLayout (self) 58 self.addPath () 59 self.list = RemoteFileList (self.layout, self) 60 self.addFileInfo () 61 self.addButtons () 62 self.WindowStuff () 63 64 if self._bridge._dataPath: 65 self.path.setPath (self._bridge._dataPath) 66 else: 67 self.Home () 68 self.ls () 69 70 # apply the layout to the dialog, then execute the dialog 71 # meaning that it will be displayed until resolved 72 self.exec_ ()
73 74 # Sets the current path to the user's home, then ls's it
75 - def Home (self):
76 self._bridge.CursorBusy () 77 self.path.setPath (self._bridge._ipyShell.ControllerCommand \ 78 ("print \"\\\\n\" + filesystem.home ()").split ("\n")[-1]) 79 self._bridge.CursorNorm () 80 self.ls ()
81 82 # Populates the lisst self.firs and self.files, then sends them to be shown 83 # based on the type of file the user currently wishes to view
84 - def ls (self):
85 outRegexp = re.compile ("\[.*\]\s*Out\s*\[.*\]:") 86 self._bridge.CursorBusy () 87 # Gets the correct path (note that the path doesn't have to be valid) 88 path = str (self.path.text ()) 89 90 hidden = "" 91 if self.hiddenCheck.checkState () == Qt.Checked: 92 hidden = ", True" 93 94 # ipython command, local, to get directories 95 result = self._bridge._ipyShell.ControllerCommand \ 96 ("print str (filesystem.lsDir (\"" + path + "\"" + hidden + "))") 97 if result.find ("rror") != -1: 98 self._bridge.Error ("BASIN_remote_file.py", "RemoteFileBrowser", "ls", result) 99 return 100 result = result.split ("\n") 101 index = 0 102 for i in xrange (len (result)): 103 if outRegexp.match (result[i]): 104 index = i 105 106 self.dirs = [] 107 for each in result[index + 1:]: 108 self.dirs.append (Path (path, each.split ("$"))) 109 110 # ipython command, local, to get all files 111 result = self._bridge._ipyShell.ControllerCommand \ 112 ("print str (filesystem.lsFile (\"" + path + "\"" + hidden + "))") 113 if result.find ("rror") != -1: 114 self._bridge.Error ("BASIN_remote_file.py", "RemoteFileBrowser", "ls", result) 115 return 116 result = result.split ("\n") 117 index = 0 118 for i in xrange (len (result)): 119 if outRegexp.match (result[i]): 120 index = i 121 122 self.files = [] 123 for each in result[index + 1:]: 124 self.files.append (Path (path, each.split ("$"))) 125 126 # sort the files alphabetically 127 # self.files.sort () 128 129 # function that will display them 130 self.showType () 131 self._bridge.CursorNorm ()
132
133 - def goActivated (self):
134 self.path.setPath (self.path.text ()) 135 self.ls ()
136 137 # Shows all directories, and the files that meet the regular expression 138 # of the type the user chose (or defaulted to) in the combobox
139 - def showType (self):
140 # wipe out the current list 141 self.Clear () 142 self.list.setSortingEnabled (False) 143 144 index = self.filetype.currentIndex () 145 variant = self.filetype.itemData (index) 146 regexp = re.compile (str (variant.toString ())) 147 tempFiles = [] 148 for file in self.files: 149 if regexp.match (str (file._name)): 150 tempFiles.append (file) 151 152 self.list.setRowCount (len (self.dirs) + len (tempFiles)) 153 154 row = 0 155 for dir in self.dirs: 156 self.addPathToList (dir, row, True) 157 row += 1 158 for file in tempFiles: 159 self.list.setRowCount (row + 1) 160 self.addPathToList (file, row, False) 161 row += 1 162 163 # Sort alphabetically by name, then by file type 164 self.list.setSortingEnabled (True) 165 self.list.resizeColumnsToContents () 166 self.list.sortItems (0) 167 self.list.sortItems (1)
168 169 # Just sets the title bar and icon of the window, overloaded for exporting
170 - def WindowStuff (self):
171 self.setWindowTitle ("Opening from " + str (self._bridge._host) + 172 ":" + str (self._bridge._port)) 173 self.setWindowIcon (QIcon (QString (Constants.ICON_DIR + "/file/open.png")))
174 175 # adds the path, and path buttons like `Go` and `Up`
176 - def addPath (self):
177 pathLayout = Constants.BQHBoxLayout (self.layout) 178 self.path = RemotePathBar (pathLayout, self) 179 self.pathButton = QPushButton ("Go") 180 self.homeButton = QPushButton (QIcon (Constants.ICON_DIR + "/misc/home.png"), "") 181 self.upButton = QPushButton (QIcon (Constants.ICON_DIR + "/misc/arrowup.png"), "") 182 pathLayout.addWidget (self.pathButton) 183 pathLayout.addWidget (self.homeButton) 184 pathLayout.addWidget (self.upButton) 185 QObject.connect (self.pathButton, SIGNAL ("clicked ()"), self.goActivated) 186 QObject.connect (self.homeButton, SIGNAL ("clicked ()"), self.Home) 187 QObject.connect (self.upButton, SIGNAL ("clicked ()"), self.path.up)
188 189 # adds the buttons import and cancel to the dialog layout
190 - def addButtons (self):
191 buttonLayout = Constants.BQHBoxLayout (self.layout) 192 self.hiddenCheck = QCheckBox ("Show Hidden") 193 QObject.connect (self.hiddenCheck, SIGNAL ("stateChanged (int)"), self.ls) 194 buttonLayout.addWidget (self.hiddenCheck) 195 196 # Make a combo box of file types and their corresponding regular expression 197 self.filetype = QComboBox () 198 QObject.connect (self.filetype, SIGNAL ("currentIndexChanged (int)"), self.showType) 199 typeLabel = QLabel ("File Type:") 200 typeLabel.setAlignment (Qt.AlignRight | Qt.AlignVCenter) 201 buttonLayout.addWidget (typeLabel) 202 buttonLayout.addWidget (self.filetype) 203 self.filetype.addItem (QString ("All Files (*.*)"), QVariant (QString (".*"))) 204 self.filetype.addItem (QString ("BASIN (*.dat, *.txt)"), QVariant (QString (".*\.(dat|txt)"))) 205 self.filetype.addItem (QString ("HDF5 (*.h5)"), QVariant (QString (".*\.h5"))) 206 self.filetype.addItem (QString ("FITS (*.fits, *.fit)"), QVariant (QString (".*\.fits?"))) 207 self.filetype.addItem (QString ("StarLab (*.strlab)"), QVariant (QString (".*\.strlab"))) 208 209 self.actionButton = QPushButton (QIcon (Constants.ICON_DIR + "/file/open.png"), QString ("Open")) 210 QObject.connect (self.actionButton, SIGNAL ("clicked ()"), self.accept) 211 buttonLayout.addWidget (self.actionButton) 212 self.cancelButton = QPushButton (QString ("Cancel")) 213 QObject.connect (self.cancelButton, SIGNAL ("clicked ()"), self.reject) 214 buttonLayout.addWidget (self.cancelButton)
215 216 # Adds the lineedit and combobox to the dialog layout
217 - def addFileInfo (self):
218 self.filename = QLineEdit (self) 219 QObject.connect (self.filename, SIGNAL ("returnPressed ()"), self.accept) 220 self.layout.addWidget (self.filename)
221 222 # Overwrites QDialog's accept function so that 223 # %autopx is restored, and the finalresult is set 224 # noting that if no file is selected, accept does nothing
225 - def accept (self):
226 items = self.list.selectedItems () 227 if (self.filename.text ()) != "": 228 self.finalresult.append (str (self.path._path) + "/" + str (self.filename.text ())) 229 elif len (items) == 1 and items[0] != None and items[0].Type () == "FILE": 230 self.finalresult.append (items[0]._path) 231 self._bridge.makeParallel () 232 QDialog.accept (self)
233 234 # Overwrites QDialog's reject function so that 235 # %autopx is restored
236 - def reject (self):
237 self._bridge.makeParallel () 238 QDialog.reject (self)
239 240 # Clear off the file/dir list, 241 # generally speaking to make room for another
242 - def Clear (self):
243 self.list.clearContents ()
244 245 # Creates the appropriate Item for the list for Files
246 - def addPathToList (self, file, row, isFolder):
247 provider = QFileIconProvider () 248 if isFolder: 249 _icon = provider.icon (QFileIconProvider.Folder) 250 _type = "Folder" 251 else: 252 _icon = provider.icon (QFileIconProvider.File) 253 _type = "file" 254 if file._name[-3:] == ".h5": 255 _type = "hdf5" 256 elif file._name[-4:] == ".fit": 257 _type = "fits" 258 elif file._name[-5:] == ".fits": 259 _type = "fits" 260 elif file._name[-4:] == ".dat": 261 _type = "basin" 262 elif file._name[-4:] == ".txt": 263 _type = "basin" 264 elif file._name[-7:] == ".strlab": 265 _type = "starlab" 266 267 cellHeight = self.list.rowHeight (0) 268 gradient = QLinearGradient (QPointF (0, 0), QPointF (0, cellHeight)) 269 gradient.setColorAt (0.0, QColor (200, 250, 200)) 270 gradient.setColorAt (0.5, Qt.white) 271 gradient.setColorAt (1.0, QColor (200, 250, 200)) 272 brush = QBrush (gradient) 273 274 name = QTableWidgetItem (_icon, file._name) 275 name.setFlags (Qt.ItemIsSelectable | Qt.ItemIsEnabled) 276 name.setTextColor (Qt.black) 277 name.setTextAlignment (Qt.AlignVCenter | Qt.AlignLeft) 278 name.setBackground (brush) 279 self.list.setItem (row, 0, name) 280 281 type = QTableWidgetItem (_type) 282 type.setFlags (Qt.ItemIsSelectable | Qt.ItemIsEnabled) 283 type.setTextColor (Qt.darkGray) 284 type.setTextAlignment (Qt.AlignVCenter | Qt.AlignRight) 285 type.setBackground (brush) 286 self.list.setItem (row, 1, type) 287 288 user = QTableWidgetItem (file._user) 289 user.setFlags (Qt.ItemIsSelectable | Qt.ItemIsEnabled) 290 user.setTextColor (Qt.darkGray) 291 user.setTextAlignment (Qt.AlignVCenter | Qt.AlignRight) 292 user.setBackground (brush) 293 self.list.setItem (row, 2, user) 294 295 group = QTableWidgetItem (file._group) 296 group.setFlags (Qt.ItemIsSelectable | Qt.ItemIsEnabled) 297 group.setTextColor (Qt.darkGray) 298 group.setTextAlignment (Qt.AlignVCenter | Qt.AlignRight) 299 group.setBackground (brush) 300 self.list.setItem (row, 3, group) 301 302 size = QTableWidgetItem (file._size) 303 size.setFlags (Qt.ItemIsSelectable | Qt.ItemIsEnabled) 304 size.setTextColor (Qt.darkGray) 305 size.setTextAlignment (Qt.AlignVCenter | Qt.AlignRight) 306 size.setBackground (brush) 307 self.list.setItem (row, 4, size) 308 309 aTime = QTableWidgetItem (file._aTime) 310 aTime.setFlags (Qt.ItemIsSelectable | Qt.ItemIsEnabled) 311 aTime.setTextColor (Qt.darkGray) 312 aTime.setTextAlignment (Qt.AlignVCenter | Qt.AlignRight) 313 aTime.setBackground (brush) 314 self.list.setItem (row, 5, aTime) 315 316 mTime = QTableWidgetItem (file._mTime) 317 mTime.setFlags (Qt.ItemIsSelectable | Qt.ItemIsEnabled) 318 mTime.setTextColor (Qt.darkGray) 319 mTime.setTextAlignment (Qt.AlignVCenter | Qt.AlignRight) 320 mTime.setBackground (brush) 321 self.list.setItem (row, 6, mTime) 322 323 cTime = QTableWidgetItem (file._cTime) 324 cTime.setFlags (Qt.ItemIsSelectable | Qt.ItemIsEnabled) 325 cTime.setTextColor (Qt.darkGray) 326 cTime.setTextAlignment (Qt.AlignVCenter | Qt.AlignRight) 327 cTime.setBackground (brush) 328 self.list.setItem (row, 7, cTime)
329 330 331 # The same basic principle as before, however a few changes were 332 # made to have it support exporting instead of importing
333 -class RemoteFileBrowserExport (RemoteFileBrowser):
334 - def __init__ (self, bridge):
335 RemoteFileBrowser.__init__ (self, bridge)
336 337 # different icon and window title for exporting done here
338 - def WindowStuff (self):
339 self.setWindowTitle ("Saving to " + str (self._bridge._host) + \ 340 ":" + str (self._bridge._port)) 341 self.setWindowIcon (QIcon (QString (Constants.ICON_DIR + "/file/save.png")))
342 343 # different buttons are created here for exporting
344 - def addButtons (self):
345 buttonLayout = Constants.BQHBoxLayout (self.layout) 346 self.hiddenCheck = QCheckBox ("Show Hidden") 347 QObject.connect (self.hiddenCheck, SIGNAL ("stateChanged (int)"), self.ls) 348 buttonLayout.addWidget (self.hiddenCheck, Qt.AlignHCenter) 349 350 # Make a combo box of file types and their corresponding regular expression 351 self.filetype = QComboBox () 352 QObject.connect (self.filetype, SIGNAL ("currentIndexChanged (int)"), self.showType) 353 typeLabel = QLabel ("File Type:") 354 typeLabel.setAlignment (Qt.AlignRight | Qt.AlignVCenter) 355 buttonLayout.addWidget (typeLabel) 356 buttonLayout.addWidget (self.filetype) 357 self.filetype.addItem (QString ("All Files (*.*)"), QVariant (QString (".*"))) 358 self.filetype.addItem (QString ("BASIN (*.dat, *.txt)"), QVariant (QString (".*\.(dat|txt)"))) 359 self.filetype.addItem (QString ("HDF5 (*.h5)"), QVariant (QString (".*\.h5"))) 360 self.filetype.addItem (QString ("FITS (*.fits, *.fit)"), QVariant (QString (".*\.fits?"))) 361 self.filetype.addItem (QString ("StarLab (*.strlab)"), QVariant (QString (".*\.strlab"))) 362 363 self.actionButton = QPushButton (QIcon (Constants.ICON_DIR + "/file/save.png"), QString ("Save")) 364 QObject.connect (self.actionButton, SIGNAL ("clicked ()"), self.accept) 365 buttonLayout.addWidget (self.actionButton, Qt.AlignHCenter) 366 self.cancelButton = QPushButton (QString ("Cancel")) 367 QObject.connect (self.cancelButton, SIGNAL ("clicked ()"), self.reject) 368 buttonLayout.addWidget (self.cancelButton, Qt.AlignHCenter)
369 370 # accept is much different from importing in that the file type you are browsing 371 # will append its extension onto your given filename (which doesn't need to exist) 372 # if it doesn't detect it being there already.
373 - def accept (self):
374 self._bridge.makeParallel () 375 item = str (self.path._path) 376 377 # if it doesn't have a leading "/", most likely the case, add it 378 if item[len (item) - 1] != "/": 379 item += "/" 380 # then tack on the file name you are accepting 381 item += str (self.filename.text ()) 382 if item.strip () != "": 383 # get the regular expression corresponding to the file type in the combobox, 384 # and if it doesn't meet the expression, then add the correct extension. 385 index = self.filetype.currentIndex () 386 variant = self.filetype.itemData (index) 387 if not re.compile (str (variant.toString ())).match (str (item)): 388 type = str (self.filetype.currentText ()) 389 if type.find == "HDF5 (*.h5)": 390 item += ".h5" 391 elif type == "BASIN (*.dat, *.txt)": 392 item += ".dat" 393 elif type == "FITS (*.fits)": 394 item += ".fits" 395 elif type == "StarLab (*.strlab)": 396 item += ".strlab" 397 # set it as the final result, and finally accept 398 self.finalresult = [item] 399 QDialog.accept (self)
400 401 # This provides a bit more convenience than a normal QLineEdit because of the builtin function self.up 402 # which essentially just chops off the last directory from the path, then recalls ls from its parent, 403 # the RemoteFileBrowser(Export). This isn't so much needed as nice to have.
404 -class RemotePathBar (QLineEdit):
405 - def __init__ (self, layout, parent):
406 QLineEdit.__init__ (self) 407 label = QLabel ("Path:") 408 layout.addWidget (label) 409 layout.addWidget (self) 410 self._parent = parent 411 self._path = ""
412
413 - def setPath (self, path):
414 self._path = path 415 self.setText (path)
416 417 # Exactly what Up ought to do when traversing files/directories... 418 # Chops the last chunk fo the path off, then goes to that new, shorter, path
419 - def up (self):
420 text = self._path 421 if text == "/": 422 return 423 if text[len (text) - 1] == "/": 424 text = text[:-1] 425 while text[len (text) - 1] != "/": 426 text = text[:-1] 427 if text != "/": 428 text = text[:-1] 429 self.setPath (text) 430 self._parent.ls ()
431 432 # This is the place where all of the actual directories and files are listed. 433 # This HAD to be a derived QListWidget because the clicking mechanisms needed 434 # to be overloaded. Other than that, in every regard it is merely a QListWidget
435 -class RemoteFileList (QTableWidget):
436 - def __init__ (self, layout, parent):
437 QTableWidget.__init__ (self) 438 self.setShowGrid (False) 439 self.setSelectionBehavior (QAbstractItemView.SelectRows) 440 self.setSelectionMode (QAbstractItemView.SingleSelection) 441 self.verticalHeader ().hide () 442 labels = ["Name", "Type", "User", "Group", "Size", "Last Access", "Last Mod", "Created On"] 443 self.setColumnCount (len (labels)) 444 self.setHorizontalHeaderLabels (labels) 445 layout.addWidget (self) 446 self._parent = parent 447 QObject.connect (self, SIGNAL ("cellDoubleClicked (int,int)"), self.DoubleClick) 448 QObject.connect (self, SIGNAL ("currentCellChanged (int,int,int,int)"), self.CellChanged)
449 450 # On a single click (note that is is also in a double click), regardless of the mouse button used, 451 # if you clicked on a file, it goes to the bar UNDER the this object, the list of files/directories, 452 # and over the combo box. This is needed mostly for exporting, which requires there to be a file 453 # in that bar, simply because you may be creating a file that exists nowhere else.
454 - def SingleClick (self, row, col):
455 if self.item (row, 1): 456 type = self.item (row, 1).text () 457 else: 458 return 459 460 if type == "Folder": 461 self._parent.filename.setText ("") 462 else: 463 item = self.item (row, 0).text () 464 self._parent.filename.setText (item)
465
466 - def CellChanged (self, newRow, newCol, prevRow, prevCol):
467 if newRow == -1: 468 newRow = 0 469 self.SingleClick (newRow, newCol)
470
471 - def keyPressEvent (self, ev):
472 key = ev.key () 473 if key == Qt.Key_Enter or key == Qt.Key_Return: 474 self.DoubleClick (self.currentRow ()) 475 elif key == Qt.Key_Up: 476 self.selectRow (self.currentRow () - 1) 477 elif key == Qt.Key_Down: 478 self.selectRow (self.currentRow () + 1)
479 480 # Note again, that a singleclick must have occurred to get here... 481 # If you double click a folder, you add that folder to the path, and 482 # begin again by calling ls (). If you double clicked a file, then you 483 # just called accept, and because a single click happenned, either a filename 484 # sits to be accepted, or nothing because folders don't add themselves to the file bar
485 - def DoubleClick (self, row, col = 0):
486 type = self.item (row, 1).text () 487 if type == "Folder": 488 item = self.item (row, 0).text () 489 if self._parent.path._path == "/": 490 self._parent.path.setPath (self._parent.path._path + item) 491 else: 492 self._parent.path.setPath (self._parent.path._path + "/" + item) 493 self._parent.ls () 494 else: 495 self._parent.accept ()
496 497
498 -class Path:
499 - def __init__ (self, path, val):
500 length = len (val) 501 502 if length >= 0: 503 self._name = val[0] 504 self._fullPath = path + "/" + self._name 505 506 if length >= 1: 507 self._user = val[1] 508 509 if length >= 2: 510 self._group = val[2] 511 512 if length >= 3: 513 self._size = val[3] 514 515 if length >= 4: 516 self._aTime = val[4] 517 518 if length >= 5: 519 self._mTime = val[5] 520 521 if length >= 6: 522 self._cTime = val[6]
523