eric6/MicroPython/MicroPythonFileManagerWidget.py

branch
micropython
changeset 7084
3eddfc540614
parent 7083
217862c28319
child 7088
e29b0ee86b29
equal deleted inserted replaced
7083:217862c28319 7084:3eddfc540614
8 """ 8 """
9 9
10 from __future__ import unicode_literals 10 from __future__ import unicode_literals
11 11
12 import os 12 import os
13 import shutil
14 import time
13 15
14 from PyQt5.QtCore import pyqtSlot, Qt, QPoint 16 from PyQt5.QtCore import pyqtSlot, Qt, QPoint
15 from PyQt5.QtWidgets import ( 17 from PyQt5.QtWidgets import (
16 QWidget, QTreeWidgetItem, QHeaderView, QMenu, QInputDialog, QLineEdit 18 QWidget, QTreeWidgetItem, QHeaderView, QMenu, QInputDialog, QLineEdit,
19 QDialog
17 ) 20 )
18 21
19 from E5Gui import E5MessageBox, E5PathPickerDialog 22 from E5Gui import E5MessageBox, E5PathPickerDialog
20 from E5Gui.E5PathPicker import E5PathPickerModes 23 from E5Gui.E5PathPicker import E5PathPickerModes
21 from E5Gui.E5FileSaveConfirmDialog import confirmOverwrite 24 from E5Gui.E5FileSaveConfirmDialog import confirmOverwrite
25 28
26 from .MicroPythonFileSystem import MicroPythonFileManager 29 from .MicroPythonFileSystem import MicroPythonFileManager
27 from .MicroPythonFileSystemUtilities import ( 30 from .MicroPythonFileSystemUtilities import (
28 mtime2string, mode2string, decoratedName, listdirStat 31 mtime2string, mode2string, decoratedName, listdirStat
29 ) 32 )
33
34 from UI.DeleteFilesConfirmationDialog import DeleteFilesConfirmationDialog
30 35
31 import UI.PixmapCache 36 import UI.PixmapCache
32 import Preferences 37 import Preferences
33 import Utilities 38 import Utilities
34 39
51 56
52 self.syncButton.setIcon(UI.PixmapCache.getIcon("2rightarrow")) 57 self.syncButton.setIcon(UI.PixmapCache.getIcon("2rightarrow"))
53 self.putButton.setIcon(UI.PixmapCache.getIcon("1rightarrow")) 58 self.putButton.setIcon(UI.PixmapCache.getIcon("1rightarrow"))
54 self.getButton.setIcon(UI.PixmapCache.getIcon("1leftarrow")) 59 self.getButton.setIcon(UI.PixmapCache.getIcon("1leftarrow"))
55 self.localUpButton.setIcon(UI.PixmapCache.getIcon("1uparrow")) 60 self.localUpButton.setIcon(UI.PixmapCache.getIcon("1uparrow"))
61 self.localReloadButton.setIcon(UI.PixmapCache.getIcon("reload"))
56 self.deviceUpButton.setIcon(UI.PixmapCache.getIcon("1uparrow")) 62 self.deviceUpButton.setIcon(UI.PixmapCache.getIcon("1uparrow"))
63 self.deviceReloadButton.setIcon(UI.PixmapCache.getIcon("reload"))
57 64
58 self.putButton.setEnabled(False) 65 self.putButton.setEnabled(False)
59 self.getButton.setEnabled(False) 66 self.getButton.setEnabled(False)
60 67
61 self.localFileTreeWidget.header().setSortIndicator( 68 self.localFileTreeWidget.header().setSortIndicator(
70 self.__fileManager.currentDir.connect(self.__handleCurrentDir) 77 self.__fileManager.currentDir.connect(self.__handleCurrentDir)
71 self.__fileManager.currentDirChanged.connect(self.__handleCurrentDir) 78 self.__fileManager.currentDirChanged.connect(self.__handleCurrentDir)
72 self.__fileManager.putFileDone.connect(self.__newDeviceList) 79 self.__fileManager.putFileDone.connect(self.__newDeviceList)
73 self.__fileManager.getFileDone.connect(self.__handleGetDone) 80 self.__fileManager.getFileDone.connect(self.__handleGetDone)
74 self.__fileManager.rsyncDone.connect(self.__handleRsyncDone) 81 self.__fileManager.rsyncDone.connect(self.__handleRsyncDone)
75 self.__fileManager.rsyncMessages.connect(self.__handleRsyncMessages)
76 self.__fileManager.rsyncProgressMessage.connect( 82 self.__fileManager.rsyncProgressMessage.connect(
77 self.__handleRsyncProgressMessage) 83 self.__handleRsyncProgressMessage)
78 self.__fileManager.removeDirectoryDone.connect(self.__newDeviceList) 84 self.__fileManager.removeDirectoryDone.connect(self.__newDeviceList)
79 self.__fileManager.createDirectoryDone.connect(self.__newDeviceList) 85 self.__fileManager.createDirectoryDone.connect(self.__newDeviceList)
80 self.__fileManager.deleteFileDone.connect(self.__newDeviceList) 86 self.__fileManager.deleteFileDone.connect(self.__newDeviceList)
91 self.__showDeviceContextMenu) 97 self.__showDeviceContextMenu)
92 98
93 self.__localMenu = QMenu(self) 99 self.__localMenu = QMenu(self)
94 self.__localMenu.addAction(self.tr("Change Directory"), 100 self.__localMenu.addAction(self.tr("Change Directory"),
95 self.__changeLocalDirectory) 101 self.__changeLocalDirectory)
96 # TODO: add some more local entries 102 self.__localMenu.addAction(
97 # TODO: add entry to reload 103 self.tr("Create Directory"), self.__createLocalDirectory)
104 self.__localDelDirTreeAct = self.__localMenu.addAction(
105 self.tr("Delete Directory Tree"), self.__deleteLocalDirectoryTree)
106 self.__localMenu.addSeparator()
107 self.__localDelFileAct = self.__localMenu.addAction(
108 self.tr("Delete File"), self.__deleteLocalFile)
109 self.__localMenu.addSeparator()
110 self.__localMenu.addAction(
111 self.tr("Show Time"), self.__showLocalTime)
98 112
99 self.__deviceMenu = QMenu(self) 113 self.__deviceMenu = QMenu(self)
100 self.__deviceMenu.addAction( 114 self.__deviceMenu.addAction(
101 self.tr("Change Directory"), self.__changeDeviceDirectory) 115 self.tr("Change Directory"), self.__changeDeviceDirectory)
102 self.__deviceMenu.addAction( 116 self.__deviceMenu.addAction(
107 self.tr("Delete Directory Tree"), self.__deleteDeviceDirectoryTree) 121 self.tr("Delete Directory Tree"), self.__deleteDeviceDirectoryTree)
108 self.__deviceMenu.addSeparator() 122 self.__deviceMenu.addSeparator()
109 self.__devDelFileAct = self.__deviceMenu.addAction( 123 self.__devDelFileAct = self.__deviceMenu.addAction(
110 self.tr("Delete File"), self.__deleteDeviceFile) 124 self.tr("Delete File"), self.__deleteDeviceFile)
111 self.__deviceMenu.addSeparator() 125 self.__deviceMenu.addSeparator()
112 # TODO: add entry to reload
113 self.__deviceMenu.addSeparator()
114 self.__deviceMenu.addAction( 126 self.__deviceMenu.addAction(
115 self.tr("Synchronize Time"), self.__synchronizeTime) 127 self.tr("Synchronize Time"), self.__synchronizeTime)
116 self.__deviceMenu.addAction( 128 self.__deviceMenu.addAction(
117 self.tr("Show Time"), self.__showDeviceTime) 129 self.tr("Show Time"), self.__showDeviceTime)
118 self.__deviceMenu.addSeparator() 130 self.__deviceMenu.addSeparator()
179 @param filesList tuple containing tuples with name, mode, size and time 191 @param filesList tuple containing tuples with name, mode, size and time
180 for each directory entry 192 for each directory entry
181 @type tuple of (str, str, str, str) 193 @type tuple of (str, str, str, str)
182 """ 194 """
183 self.deviceFileTreeWidget.clear() 195 self.deviceFileTreeWidget.clear()
184 for name, mode, size, time in filesList: 196 for name, mode, size, dateTime in filesList:
185 itm = QTreeWidgetItem(self.deviceFileTreeWidget, 197 itm = QTreeWidgetItem(self.deviceFileTreeWidget,
186 [name, mode, size, time]) 198 [name, mode, size, dateTime])
187 itm.setTextAlignment(1, Qt.AlignHCenter) 199 itm.setTextAlignment(1, Qt.AlignHCenter)
188 itm.setTextAlignment(2, Qt.AlignRight) 200 itm.setTextAlignment(2, Qt.AlignRight)
189 self.deviceFileTreeWidget.header().resizeSections( 201 self.deviceFileTreeWidget.header().resizeSections(
190 QHeaderView.ResizeToContents) 202 QHeaderView.ResizeToContents)
191 203
256 """ 268 """
257 cwd = self.localCwd.text() 269 cwd = self.localCwd.text()
258 dirname = os.path.dirname(cwd) 270 dirname = os.path.dirname(cwd)
259 self.__listLocalFiles(dirname) 271 self.__listLocalFiles(dirname)
260 272
273 @pyqtSlot()
274 def on_localReloadButton_clicked(self):
275 """
276 Private slot to reload the local list.
277 """
278 dirname = self.localCwd.text()
279 self.__listLocalFiles(dirname)
280
261 @pyqtSlot(QTreeWidgetItem, int) 281 @pyqtSlot(QTreeWidgetItem, int)
262 def on_deviceFileTreeWidget_itemActivated(self, item, column): 282 def on_deviceFileTreeWidget_itemActivated(self, item, column):
263 """ 283 """
264 Private slot to handle the activation of a device item. 284 Private slot to handle the activation of a device item.
265 285
294 Private slot to go up one directory level on the device. 314 Private slot to go up one directory level on the device.
295 """ 315 """
296 cwd = self.deviceCwd.text() 316 cwd = self.deviceCwd.text()
297 dirname = os.path.dirname(cwd) 317 dirname = os.path.dirname(cwd)
298 self.__fileManager.cd(dirname) 318 self.__fileManager.cd(dirname)
319
320 @pyqtSlot()
321 def on_deviceReloadButton_clicked(self):
322 """
323 Private slot to reload the device list.
324 """
325 dirname = self.deviceCwd.text()
326 if dirname:
327 self.__fileManager.lls(dirname)
328 else:
329 self.__fileManager.pwd()
299 330
300 def __isFileInList(self, filename, treeWidget): 331 def __isFileInList(self, filename, treeWidget):
301 """ 332 """
302 Private method to check, if a file name is contained in a tree widget. 333 Private method to check, if a file name is contained in a tree widget.
303 334
325 filename = selectedItems[0].text(0).strip() 356 filename = selectedItems[0].text(0).strip()
326 if not filename.endswith("/"): 357 if not filename.endswith("/"):
327 # it is really a file 358 # it is really a file
328 if self.__isFileInList(filename, self.deviceFileTreeWidget): 359 if self.__isFileInList(filename, self.deviceFileTreeWidget):
329 # ask for overwrite permission 360 # ask for overwrite permission
330 # TODO: test this
331 action, resultFilename = confirmOverwrite( 361 action, resultFilename = confirmOverwrite(
332 filename, self.tr("Copy File to Device"), 362 filename, self.tr("Copy File to Device"),
333 self.tr("The given file exists already" 363 self.tr("The given file exists already"
334 " (Enter file name only)."), 364 " (Enter file name only)."),
335 False, self) 365 False, self)
357 filename = selectedItems[0].text(0).strip() 387 filename = selectedItems[0].text(0).strip()
358 if not filename.endswith("/"): 388 if not filename.endswith("/"):
359 # it is really a file 389 # it is really a file
360 if self.__isFileInList(filename, self.localFileTreeWidget): 390 if self.__isFileInList(filename, self.localFileTreeWidget):
361 # ask for overwrite permission 391 # ask for overwrite permission
362 # TODO: test this
363 action, resultFilename = confirmOverwrite( 392 action, resultFilename = confirmOverwrite(
364 filename, self.tr("Copy File from Device"), 393 filename, self.tr("Copy File from Device"),
365 self.tr("The given file exists already."), 394 self.tr("The given file exists already."),
366 True, self) 395 True, self)
367 if action == "cancel": 396 if action == "cancel":
391 @param localFile name of the local file 420 @param localFile name of the local file
392 @type str 421 @type str
393 """ 422 """
394 self.__listLocalFiles(self.localCwd.text()) 423 self.__listLocalFiles(self.localCwd.text())
395 424
396 # TODO: test this
397 @pyqtSlot() 425 @pyqtSlot()
398 def on_syncButton_clicked(self): 426 def on_syncButton_clicked(self):
399 """ 427 """
400 Private slot to synchronize the local directory to the device. 428 Private slot to synchronize the local directory to the device.
401 """ 429 """
416 @type str 444 @type str
417 """ 445 """
418 self.__listLocalFiles(self.localCwd.text()) 446 self.__listLocalFiles(self.localCwd.text())
419 self.__fileManager.lls(self.deviceCwd.text()) 447 self.__fileManager.lls(self.deviceCwd.text())
420 448
421 @pyqtSlot(list)
422 def __handleRsyncMessages(self, messages):
423 """
424 Private slot to handle messages from the rsync operation.
425
426 @param messages list of message generated by the rsync operation
427 @type list
428 """
429 E5MessageBox.information(
430 self,
431 self.tr("rsync Messages"),
432 self.tr("""<p>rsync gave the following messages</p>"""
433 """<ul><li>{0}</li></ul>""").format(
434 "</li><li>".join(messages)
435 )
436 )
437
438 @pyqtSlot(str) 449 @pyqtSlot(str)
439 def __handleRsyncProgressMessage(self, message): 450 def __handleRsyncProgressMessage(self, message):
440 """ 451 """
441 Private slot handling progress messages sent by the file manager. 452 Private slot handling progress messages sent by the file manager.
442 """ 453
443 # TODO: not implemented yet 454 @param message message to be shown
444 # create and open the info dialog, if it is None 455 @type str
445 # connect to the dialog finished(int) signal to set the memeber variable to None 456 """
446 # add messages to dialog 457 if self.__progressInfoDialog is None:
458 from .MicroPythonProgressInfoDialog import (
459 MicroPythonProgressInfoDialog
460 )
461 self.__progressInfoDialog = MicroPythonProgressInfoDialog(self)
462 self.__progressInfoDialog.finished.connect(
463 self.__progressInfoDialogFinished)
464 self.__progressInfoDialog.show()
465 self.__progressInfoDialog.addMessage(message)
466
467 @pyqtSlot()
468 def __progressInfoDialogFinished(self):
469 """
470 Private slot handling the closing of the progress info dialog.
471 """
472 self.__progressInfoDialog.deleteLater()
473 self.__progressInfoDialog = None
447 474
448 @pyqtSlot() 475 @pyqtSlot()
449 def __newDeviceList(self): 476 def __newDeviceList(self):
450 """ 477 """
451 Private slot to initiate a new long list of the device directory. 478 Private slot to initiate a new long list of the device directory.
462 Private slot to show the REPL context menu. 489 Private slot to show the REPL context menu.
463 490
464 @param pos position to show the menu at 491 @param pos position to show the menu at
465 @type QPoint 492 @type QPoint
466 """ 493 """
494 hasSelection = bool(len(self.localFileTreeWidget.selectedItems()))
495 if hasSelection:
496 name = self.localFileTreeWidget.selectedItems()[0].text(0)
497 isDir = name.endswith("/")
498 isFile = not isDir
499 else:
500 isDir = False
501 isFile = False
502 self.__localDelDirTreeAct.setEnabled(isDir)
503 self.__localDelFileAct.setEnabled(isFile)
504
467 self.__localMenu.exec_(self.localFileTreeWidget.mapToGlobal(pos)) 505 self.__localMenu.exec_(self.localFileTreeWidget.mapToGlobal(pos))
468 506
469 @pyqtSlot() 507 @pyqtSlot()
470 def __changeLocalDirectory(self): 508 def __changeLocalDirectory(self):
471 """ 509 """
482 if not os.path.isabs(dirPath): 520 if not os.path.isabs(dirPath):
483 dirPath = os.path.join(self.localCwd.text(), dirPath) 521 dirPath = os.path.join(self.localCwd.text(), dirPath)
484 self.localCwd.setText(dirPath) 522 self.localCwd.setText(dirPath)
485 self.__listLocalFiles(dirPath) 523 self.__listLocalFiles(dirPath)
486 524
525 # TODO: test this
526 @pyqtSlot()
527 def __createLocalDirectory(self):
528 """
529 Private slot to create a local directory.
530 """
531 dirPath, ok = QInputDialog.getText(
532 self,
533 self.tr("Create Directory"),
534 self.tr("Enter directory name:"),
535 QLineEdit.Normal)
536 if ok and dirPath:
537 dirPath = os.path.join(self.localCwd.text(), dirPath)
538 try:
539 os.mkdir(dirPath)
540 except (OSError, IOError) as exc:
541 E5MessageBox.critical(
542 self,
543 self.tr("Create Directory"),
544 self.tr("""<p>The directory <b>{0}</b> could not be"""
545 """ created.</p><p>Reason: {1}</p>""").format(
546 dirPath, str(exc))
547 )
548
549 # TODO: test this
550 @pyqtSlot()
551 def __deleteLocalDirectoryTree(self):
552 """
553 Private slot to delete a local directory tree.
554 """
555 if bool(len(self.localFileTreeWidget.selectedItems())):
556 name = self.localFileTreeWidget.selectedItems()[0].text(0)
557 dirname = os.path.join(self.localCwd.text(), name[:-1])
558 dlg = DeleteFilesConfirmationDialog(
559 self,
560 self.tr("Delete Directory Tree"),
561 self.tr(
562 "Do you really want to delete this directory tree?"),
563 [dirname])
564 if dlg.exec_() == QDialog.Accepted:
565 try:
566 shutil.rmtree(dirname)
567 except Exception as exc:
568 E5MessageBox.critical(
569 self,
570 self.tr("Delete Directory Tree"),
571 self.tr("""<p>The directory <b>{0}</b> could not be"""
572 """ deleted.</p><p>Reason: {1}</p>""").format(
573 dirname, str(exc))
574 )
575
576 # TODO: test this
577 @pyqtSlot()
578 def __deleteLocalFile(self):
579 """
580 Private slot to delete a local file.
581 """
582 if bool(len(self.localFileTreeWidget.selectedItems())):
583 name = self.localFileTreeWidget.selectedItems()[0].text(0)
584 filename = os.path.join(self.localCwd.text(), name)
585 dlg = DeleteFilesConfirmationDialog(
586 self,
587 self.tr("Delete File"),
588 self.tr(
589 "Do you really want to delete this file?"),
590 [filename])
591 if dlg.exec_() == QDialog.Accepted:
592 try:
593 os.remove(filename)
594 except (OSError, IOError) as exc:
595 E5MessageBox.critical(
596 self,
597 self.tr("Delete File"),
598 self.tr("""<p>The file <b>{0}</b> could not be"""
599 """ deleted.</p><p>Reason: {1}</p>""").format(
600 filename, str(exc))
601 )
602
603 # TODO: test this
604 @pyqtSlot()
605 def __showLocalTime(self):
606 """
607 Private slot to show the local date and time.
608 """
609 localdatetime = time.localtime()
610 loacldate = time.strftime('%Y-%m-%d', localdatetime)
611 localtime = time.strftime('%H:%M:%S', localdatetime)
612 E5MessageBox.information(
613 self,
614 self.tr("Local Date and Time"),
615 self.tr("<h3>Local Date and Time</h3>"
616 "<table>"
617 "<tr><td><b>Date</b></td><td>{0}</td></tr>"
618 "<tr><td><b>Time</b></td><td>{1}</td></tr>"
619 "</table>"
620 ).format(loacldate, localtime)
621 )
622
487 ################################################################## 623 ##################################################################
488 ## Context menu methods for the device files below 624 ## Context menu methods for the device files below
489 ################################################################## 625 ##################################################################
490 626
491 @pyqtSlot(QPoint) 627 @pyqtSlot(QPoint)
550 Private slot to delete an empty directory on the device. 686 Private slot to delete an empty directory on the device.
551 """ 687 """
552 if bool(len(self.deviceFileTreeWidget.selectedItems())): 688 if bool(len(self.deviceFileTreeWidget.selectedItems())):
553 name = self.deviceFileTreeWidget.selectedItems()[0].text(0) 689 name = self.deviceFileTreeWidget.selectedItems()[0].text(0)
554 dirname = self.deviceCwd.text() + "/" + name[:-1] 690 dirname = self.deviceCwd.text() + "/" + name[:-1]
555 # TODO: add confirmation 691 dlg = DeleteFilesConfirmationDialog(
556 self.__fileManager.rmdir(dirname) 692 self,
693 self.tr("Delete Directory"),
694 self.tr(
695 "Do you really want to delete this directory?"),
696 [dirname])
697 if dlg.exec_() == QDialog.Accepted:
698 self.__fileManager.rmdir(dirname)
557 699
558 @pyqtSlot() 700 @pyqtSlot()
559 def __deleteDeviceDirectoryTree(self): 701 def __deleteDeviceDirectoryTree(self):
560 """ 702 """
561 Private slot to delete a directory and all its subdirectories 703 Private slot to delete a directory and all its subdirectories
562 recursively. 704 recursively.
563 """ 705 """
564 if bool(len(self.deviceFileTreeWidget.selectedItems())): 706 if bool(len(self.deviceFileTreeWidget.selectedItems())):
565 name = self.deviceFileTreeWidget.selectedItems()[0].text(0) 707 name = self.deviceFileTreeWidget.selectedItems()[0].text(0)
566 dirname = self.deviceCwd.text() + "/" + name[:-1] 708 dirname = self.deviceCwd.text() + "/" + name[:-1]
567 # TODO: add confirmation 709 dlg = DeleteFilesConfirmationDialog(
568 self.__fileManager.rmdir(dirname, recursive=True) 710 self,
711 self.tr("Delete Directory Tree"),
712 self.tr(
713 "Do you really want to delete this directory tree?"),
714 [dirname])
715 if dlg.exec_() == QDialog.Accepted:
716 self.__fileManager.rmdir(dirname, recursive=True)
569 717
570 @pyqtSlot() 718 @pyqtSlot()
571 def __deleteDeviceFile(self): 719 def __deleteDeviceFile(self):
572 """ 720 """
573 Private slot to delete a file 721 Private slot to delete a file.
574 """ 722 """
575 if bool(len(self.deviceFileTreeWidget.selectedItems())): 723 if bool(len(self.deviceFileTreeWidget.selectedItems())):
576 name = self.deviceFileTreeWidget.selectedItems()[0].text(0) 724 name = self.deviceFileTreeWidget.selectedItems()[0].text(0)
577 filename = self.deviceCwd.text() + "/" + name 725 filename = self.deviceCwd.text() + "/" + name
578 # TODO: add confirmation 726 dlg = DeleteFilesConfirmationDialog(
579 self.__fileManager.delete(filename) 727 self,
728 self.tr("Delete File"),
729 self.tr(
730 "Do you really want to delete this file?"),
731 [filename])
732 if dlg.exec_() == QDialog.Accepted:
733 self.__fileManager.delete(filename)
580 734
581 @pyqtSlot() 735 @pyqtSlot()
582 def __synchronizeTime(self): 736 def __synchronizeTime(self):
583 """ 737 """
584 Private slot to synchronize the local time to the device. 738 Private slot to synchronize the local time to the device.

eric ide

mercurial