--- a/eric6/MicroPython/MicroPythonFileManagerWidget.py Mon Jul 22 20:17:33 2019 +0200 +++ b/eric6/MicroPython/MicroPythonFileManagerWidget.py Tue Jul 23 19:43:14 2019 +0200 @@ -19,8 +19,9 @@ from .Ui_MicroPythonFileManagerWidget import Ui_MicroPythonFileManagerWidget -from .MicroPythonFileSystem import ( - MicroPythonFileManager, decoratedName, mode2string, mtime2string +from .MicroPythonFileSystem import MicroPythonFileManager +from .MicroPythonFileSystemUtilities import ( + mtime2string, mode2string, decoratedName, listdirStat ) import UI.PixmapCache @@ -44,6 +45,7 @@ super(MicroPythonFileManagerWidget, self).__init__(parent) self.setupUi(self) + self.syncButton.setIcon(UI.PixmapCache.getIcon("2rightarrow")) self.putButton.setIcon(UI.PixmapCache.getIcon("1rightarrow")) self.getButton.setIcon(UI.PixmapCache.getIcon("1leftarrow")) self.localUpButton.setIcon(UI.PixmapCache.getIcon("1uparrow")) @@ -61,9 +63,35 @@ self.__fileManager.longListFiles.connect(self.__handleLongListFiles) self.__fileManager.currentDir.connect(self.__handleCurrentDir) + self.__fileManager.currentDirChanged.connect(self.__handleCurrentDir) + self.__fileManager.putFileDone.connect(self.__handlePutDone) + self.__fileManager.getFileDone.connect(self.__handleGetDone) + self.__fileManager.rsyncDone.connect(self.__handleRsyncDone) + self.__fileManager.rsyncMessages.connect(self.__handleRsyncMessages) self.__fileManager.longListFilesFailed.connect(self.__handleError) self.__fileManager.currentDirFailed.connect(self.__handleError) + self.__fileManager.currentDirChangeFailed.connect(self.__handleError) + self.__fileManager.putFileFailed.connect(self.__handleError) + self.__fileManager.getFileFailed.connect(self.__handleError) + self.__fileManager.rsyncFailed.connect(self.__handleError) + + # TODO: add context menus for panes (separate menus) + # local pane: + # Change Directory + # + # device pane: + # Change Directory + # Create Directory + # Delete Directory + # Delete Directory Tree (= recursive delete) + # ---------------------------- + # Delete File + # ---------------------------- + # Synchronize Time + # Show Time + # ---------------------------- + # Show Version def start(self): """ @@ -139,28 +167,6 @@ @param dirname name of the local directory to be listed @type str """ # __IGNORE_WARNING_D234__ - def isvisible(name): - return not name.startswith(".") and not name.endswith("~") - - def stat(filename): - try: - rstat = os.lstat(filename) - except Exception: - rstat = os.stat(filename) - return tuple(rstat) - - def listdir_stat(dirname): - try: - if dirname: - files = os.listdir(dirname) - else: - files = os.listdir() - except OSError: - return [] - if dirname in ('', '/'): - return [(f, stat(f)) for f in files if isvisible(f)] - return [(f, stat(os.path.join(dirname, f))) for f in files - if isvisible(f)] if not dirname: dirname = os.getcwd() @@ -168,7 +174,7 @@ dirname = dirname[:-1] self.localCwd.setText(dirname) - filesStatList = listdir_stat(dirname) + filesStatList = listdirStat(dirname) filesList = [( decoratedName(f, s[0], os.path.isdir(os.path.join(dirname, f))), mode2string(s[0]), @@ -198,7 +204,7 @@ name = os.path.join(self.localCwd.text(), item.text(0)) if name.endswith("/"): # directory names end with a '/' - self.__listLocalFiles(name) + self.__listLocalFiles(name[:-1]) elif Utilities.MimeTypes.isTextFile(name): e5App().getObject("ViewManager").getEditor(name) @@ -226,41 +232,177 @@ @pyqtSlot(QTreeWidgetItem, int) def on_deviceFileTreeWidget_itemActivated(self, item, column): """ - Slot documentation goes here. + Private slot to handle the activation of a device item. - @param item DESCRIPTION + If the item is a directory, the current working directory is changed + and the list will be re-populated for this directory. + + @param item reference to the activated item @type QTreeWidgetItem - @param column DESCRIPTION + @param column column of the activation @type int """ - # TODO: not implemented yet - # chdir to activated directory triggering a pwd triggering a lls + name = os.path.join(self.deviceCwd.text(), item.text(0)) + if name.endswith("/"): + # directory names end with a '/' + self.__fileManager.cd(name[:-1]) @pyqtSlot() def on_deviceFileTreeWidget_itemSelectionChanged(self): """ - Slot documentation goes here. + Private slot handling a change of selection in the local pane. """ - # TODO: not implemented yet + enable = bool(len(self.deviceFileTreeWidget.selectedItems())) + if enable: + enable &= not ( + self.deviceFileTreeWidget.selectedItems()[0].text(0) + .endswith("/")) + self.getButton.setEnabled(enable) @pyqtSlot() def on_deviceUpButton_clicked(self): """ - Slot documentation goes here. + Private slot to go up one directory level on the device. + """ + cwd = self.deviceCwd.text() + dirname = os.path.dirname(cwd) + self.__fileManager.cd(dirname) + + def __isFileInList(self, filename, treeWidget): """ - # TODO: not implemented yet - raise NotImplementedError + Private method to check, if a file name is contained in a tree widget. + + @param filename name of the file to check + @type str + @param treeWidget reference to the tree widget to be checked against + @return flag indicating that the file name is present + @rtype bool + """ + itemCount = treeWidget.topLevelItemCount() + if itemCount: + for row in range(itemCount): + if treeWidget.topLevelItem(row).text(0) == filename: + return True + + return False @pyqtSlot() def on_putButton_clicked(self): """ - Slot documentation goes here. + Private slot to copy the selected file to the connected device. """ - # TODO: not implemented yet + selectedItems = self.localFileTreeWidget.selectedItems() + if selectedItems: + filename = selectedItems[0].text(0).strip() + if not filename.endswith("/"): + # it is really a file + if self.__isFileInList(filename, self.deviceFileTreeWidget): + # ask for overwrite permission + ok = E5MessageBox.yesNo( + self, + self.tr("Copy File to Device"), + self.tr("<p>The file <b>{0}</b> exists on the" + " connected device. Overwrite it?</p>") + .format(filename) + ) + if not ok: + return + # TODO: allow to rename the new file + + self.__fileManager.put( + os.path.join(self.localCwd.text(), filename), + os.path.join(self.deviceCwd.text(), filename) + ) @pyqtSlot() def on_getButton_clicked(self): """ - Slot documentation goes here. + Private slot to copy the selected file from the connected device. + """ + selectedItems = self.deviceFileTreeWidget.selectedItems() + if selectedItems: + filename = selectedItems[0].text(0).strip() + if not filename.endswith("/"): + # it is really a file + if self.__isFileInList(filename, self.localFileTreeWidget): + # ask for overwrite permission + ok = E5MessageBox.yesNo( + self, + self.tr("Copy File from Device"), + self.tr("<p>The file <b>{0}</b> exists locally." + " Overwrite it?</p>") + .format(filename) + ) + if not ok: + return + # TODO: allow to rename the new file + + self.__fileManager.get( + os.path.join(self.deviceCwd.text(), filename), + os.path.join(self.localCwd.text(), filename) + ) + + @pyqtSlot(str, str) + def __handlePutDone(self, localFile, deviceFile): + """ + Private slot handling a successful copy of a file to the device. + + @param localFile name of the local file + @type str + @param deviceFile name of the file on the device + @type str + """ + self.__fileManager.lls(self.deviceCwd.text()) + + @pyqtSlot(str, str) + def __handleGetDone(self, deviceFile, localFile): """ - # TODO: not implemented yet + Private slot handling a successful copy of a file from the device. + + @param deviceFile name of the file on the device + @type str + @param localFile name of the local file + @type str + """ + self.__listLocalFiles(self.localCwd.text()) + + @pyqtSlot() + def on_syncButton_clicked(self): + """ + Private slot to synchronize the local directory to the device. + """ + self.__fileManager.rsync( + self.localCwd.text(), + self.deviceCwd.text(), + mirror=True + ) + + @pyqtSlot(str, str) + def __handleRsyncDone(self, localDir, deviceDir): + """ + Private method to handle the completion of the rsync operation. + + @param localDir name of the local directory + @type str + @param deviceDir name of the device directory + @type str + """ + self.__listLocalFiles(self.localCwd.text()) + self.__fileManager.lls(self.deviceCwd.text()) + + @pyqtSlot(list) + def __handleRsyncMessages(self, messages): + """ + Private slot to handle messages from the rsync operation. + + @param messages list of message generated by the rsync operation + @type list + """ + E5MessageBox.information( + self, + self.tr("rsync Messages"), + self.tr("""<p>rsync gave the following messages</p>""" + """<ul><li>{0}</li></ul>""").format( + "</li><li>".join(messages) + ) + )