Sat, 27 Jul 2019 17:02:01 +0200
MicroPython: continued implementing the file manager widget.
eric6/MicroPython/MicroPythonFileManagerWidget.py | file | annotate | diff | comparison | revisions | |
eric6/MicroPython/MicroPythonFileSystem.py | file | annotate | diff | comparison | revisions |
--- a/eric6/MicroPython/MicroPythonFileManagerWidget.py Sat Jul 27 15:43:21 2019 +0200 +++ b/eric6/MicroPython/MicroPythonFileManagerWidget.py Sat Jul 27 17:02:01 2019 +0200 @@ -36,6 +36,7 @@ import UI.PixmapCache import Preferences import Utilities +import Globals class MicroPythonFileManagerWidget(QWidget, Ui_MicroPythonFileManagerWidget): @@ -84,10 +85,13 @@ self.__fileManager.removeDirectoryDone.connect(self.__newDeviceList) self.__fileManager.createDirectoryDone.connect(self.__newDeviceList) self.__fileManager.deleteFileDone.connect(self.__newDeviceList) + self.__fileManager.fsinfoDone.connect(self.__shownFsInfoResult) self.__fileManager.synchTimeDone.connect(self.__timeSynchronized) self.__fileManager.showTimeDone.connect(self.__deviceTimeReceived) self.__fileManager.showVersionDone.connect( self.__deviceVersionReceived) + self.__fileManager.showImplementationDone.connect( + self.__deviceImplementationReceived) self.__fileManager.error.connect(self.__handleError) @@ -124,21 +128,31 @@ self.tr("Delete File"), self.__deleteDeviceFile) self.__deviceMenu.addSeparator() self.__deviceMenu.addAction( + self.tr("Show Filesystem Info"), self.__showFileSystemInfo) + self.__deviceMenu.addSeparator() + self.__deviceMenu.addAction( self.tr("Synchronize Time"), self.__synchronizeTime) self.__deviceMenu.addAction( self.tr("Show Time"), self.__showDeviceTime) self.__deviceMenu.addSeparator() self.__deviceMenu.addAction( self.tr("Show Version"), self.__showDeviceVersion) + self.__deviceMenu.addAction( + self.tr("Show Implementation"), self.__showImplementation) def start(self): """ Public method to start the widget. """ - self.__fileManager.connect() + ui = e5App().getObject("UserInterface") + vm = e5App().getObject("ViewManager") + + ui.preferencesChanged.connect( + self.__fileManager.handlePreferencesChanged) + + self.__fileManager.connectToDevice() dirname = "" - vm = e5App().getObject("ViewManager") aw = vm.activeWindow() if aw: dirname = os.path.dirname(aw.getFileName()) @@ -153,7 +167,11 @@ """ Public method to stop the widget. """ - self.__fileManager.disconnect() + ui = e5App().getObject("UserInterface") + ui.preferencesChanged.disconnect( + self.__fileManager.handlePreferencesChanged) + + self.__fileManager.disconnectFromDevice() @pyqtSlot(str, str) def __handleError(self, method, error): @@ -522,7 +540,6 @@ self.localCwd.setText(dirPath) self.__listLocalFiles(dirPath) - # TODO: test this @pyqtSlot() def __createLocalDirectory(self): """ @@ -537,6 +554,7 @@ dirPath = os.path.join(self.localCwd.text(), dirPath) try: os.mkdir(dirPath) + self.__listLocalFiles(self.localCwd.text()) except (OSError, IOError) as exc: E5MessageBox.critical( self, @@ -546,7 +564,6 @@ dirPath, str(exc)) ) - # TODO: test this @pyqtSlot() def __deleteLocalDirectoryTree(self): """ @@ -564,6 +581,7 @@ if dlg.exec_() == QDialog.Accepted: try: shutil.rmtree(dirname) + self.__listLocalFiles(self.localCwd.text()) except Exception as exc: E5MessageBox.critical( self, @@ -573,7 +591,6 @@ dirname, str(exc)) ) - # TODO: test this @pyqtSlot() def __deleteLocalFile(self): """ @@ -591,6 +608,7 @@ if dlg.exec_() == QDialog.Accepted: try: os.remove(filename) + self.__listLocalFiles(self.localCwd.text()) except (OSError, IOError) as exc: E5MessageBox.critical( self, @@ -600,7 +618,6 @@ filename, str(exc)) ) - # TODO: test this @pyqtSlot() def __showLocalTime(self): """ @@ -665,7 +682,6 @@ dirPath = self.deviceCwd.text() + "/" + dirPath self.__fileManager.cd(dirPath) - # TODO: test this @pyqtSlot() def __createDeviceDirectory(self): """ @@ -679,7 +695,6 @@ if ok and dirPath: self.__fileManager.mkdir(dirPath) - # TODO: test this @pyqtSlot() def __deleteDeviceDirectory(self): """ @@ -733,6 +748,41 @@ self.__fileManager.delete(filename) @pyqtSlot() + def __showFileSystemInfo(self): + """ + Private slot to show some file system information. + """ + self.__fileManager.fileSystemInfo() + + @pyqtSlot(tuple) + def __shownFsInfoResult(self, fsinfo): + """ + Private slot to show the file systom information of the device. + + @param fsinfo tuple of tuples containing the file system name, the + total size, the used size and the free size + @type tuple of tuples of (str, int, int, int) + """ + msg = self.tr("<h3>Filesystem Information</h3>") + for name, totalSize, usedSize, freeSize in fsinfo: + msg += self.tr( + "<h4>{0}</h4" + "<table>" + "<tr><td>Total Size: </td><td align='right'>{1}</td></tr>" + "<tr><td>Used Size: </td><td align='right'>{2}</td></tr>" + "<tr><td>Free Size: </td><td align='right'>{3}</td></tr>" + "</table>" + ).format(name, + Globals.dataString(totalSize), + Globals.dataString(usedSize), + Globals.dataString(freeSize), + ) + E5MessageBox.information( + self, + self.tr("Filesystem Information"), + msg) + + @pyqtSlot() def __synchronizeTime(self): """ Private slot to synchronize the local time to the device. @@ -814,3 +864,29 @@ self, self.tr("Device Version Information"), msg) + + @pyqtSlot() + def __showImplementation(self): + """ + Private slot to show some implementation related information. + """ + self.__fileManager.showImplementation() + + @pyqtSlot(str, str) + def __deviceImplementationReceived(self, name, version): + """ + Privat slot handling the receipt of implementation info. + + @param name name of the implementation + @type str + @param version version string of the implementation + @type str + """ + E5MessageBox.information( + self, + self.tr("Device Implementation Information"), + self.tr( + "<h3>Device Implementation Information</h3>" + "<p>This device contains <b>{0} {1}</b>.</p>" + ).format(name, version) + )
--- a/eric6/MicroPython/MicroPythonFileSystem.py Sat Jul 27 15:43:21 2019 +0200 +++ b/eric6/MicroPython/MicroPythonFileSystem.py Sat Jul 27 17:02:01 2019 +0200 @@ -21,6 +21,8 @@ mtime2string, mode2string, decoratedName, listdirStat ) +import Preferences + class MicroPythonFileSystem(QObject): """ @@ -488,6 +490,48 @@ hostFile.write(out) return True + def fileSystemInfo(self): + """ + Public method to obtain information about the currently mounted file + systems. + + @return tuple of tuples containing the file system name, the total + size, the used size and the free size + @rtype tuple of tuples of (str, int, int, int) + """ + commands = [ + "import os", + "\n".join([ + "def fsinfo():", + " infolist = []", + " fsnames = os.listdir('/')", + " for fs in fsnames:", + " fs = '/' + fs", + " infolist.append((fs, os.statvfs(fs)))", + " return infolist", + ]), + "print(fsinfo())", + ] + out, err = self.__execute(commands) + if err: + raise IOError(self.__shortError(err)) + infolist = ast.literal_eval(out.decode("utf-8")) + if infolist is None: + return None + else: + filesystemInfos = [] + for fs, info in infolist: + totalSize = info[2] * info[1] + freeSize = info[4] * info[1] + usedSize = totalSize - freeSize + filesystemInfos.append((fs, totalSize, usedSize, freeSize)) + + return tuple(filesystemInfos) + + ################################################################## + ## non-filesystem related methods below + ################################################################## + def version(self): """ Public method to get the MicroPython version information of the @@ -495,26 +539,15 @@ @return dictionary containing the version information @rtype dict - @exception ValueError raised to indicate that the device might not be - running MicroPython or there was an issue parsing the output + @exception IOError raised to indicate an issue with the device """ commands = [ "import os", "print(os.uname())", ] - try: - out, err = self.__execute(commands) - if err: - raise ValueError(self.__shortError(err)) - except ValueError: - # just re-raise it - raise - except Exception: - # Raise a value error to indicate being unable to find something - # on the device that will return parseable information about the - # version. It doesn't matter what the error is, it just needs to - # report a failure with the expected ValueError exception. - raise ValueError("Unable to determine version information.") + out, err = self.__execute(commands) + if err: + raise IOError(self.__shortError(err)) rawOutput = out.decode("utf-8").strip() rawOutput = rawOutput[1:-1] @@ -525,6 +558,38 @@ result[key.strip()] = value.strip()[1:-1] return result + def getImplementation(self): + """ + Public method to get some implementation information of the connected + device. + + @return dictionary containing the implementation information + @rtype dict + @exception IOError raised to indicate an issue with the device + """ + commands = [ + "import sys", + "res = {}", + "\n".join([ + "try:", + " res['name'] = sys.implementation.name", + "except AttributeError:", + " res['name'] = 'unknown'", + ]), + "\n".join([ + "try:", + " res['version'] = '.'.join((str(i) for i in" + " sys.implementation.version))", + "except AttributeError:", + " res['version'] = 'unknown'", + ]), + "print(res)", + ] + out, err = self.__execute(commands) + if err: + raise IOError(self.__shortError(err)) + return ast.literal_eval(out.decode("utf-8")) + def syncTime(self): """ Public method to set the time of the connected device to the local @@ -611,12 +676,17 @@ rsync is doing @signal removeDirectoryDone() emitted after a directory has been deleted @signal createDirectoryDone() emitted after a directory was created + @signal fsinfoDone(fsinfo) emitted after the file system information was + obtained + @signal synchTimeDone() emitted after the time was synchronizde to the device @signal showTimeDone(dateTime) emitted after the date and time was fetched from the connected device @signal showVersionDone(versionInfo) emitted after the version information was fetched from the connected device + @signal showImplementationDone(name,version) emitted after the + implementation information has been obtained @signal error(exc) emitted with a failure message to indicate a failure during the most recent operation @@ -631,9 +701,12 @@ rsyncProgressMessage = pyqtSignal(str) removeDirectoryDone = pyqtSignal() createDirectoryDone = pyqtSignal() + fsinfoDone = pyqtSignal(tuple) + synchTimeDone = pyqtSignal() showTimeDone = pyqtSignal(str) showVersionDone = pyqtSignal(dict) + showImplementationDone = pyqtSignal(str, str) error = pyqtSignal(str, str) @@ -649,11 +722,13 @@ super(MicroPythonFileManager, self).__init__(parent) self.__serialPort = port - self.__serial = MicroPythonSerialPort(parent=self) + self.__serial = MicroPythonSerialPort( + timeout=Preferences.getMicroPython("SerialTimeout"), + parent=self) self.__fs = MicroPythonFileSystem(parent=self) @pyqtSlot() - def connect(self): + def connectToDevice(self): """ Public slot to start the manager. """ @@ -661,12 +736,19 @@ self.__fs.setSerial(self.__serial) @pyqtSlot() - def disconnect(self): + def disconnectFromDevice(self): """ Public slot to stop the thread. """ self.__serial.closeSerialLink() + @pyqtSlot() + def handlePreferencesChanged(self): + """ + Public slot to handle a change of the preferences. + """ + self.__serial.setTimeout(Preferences.getMicroPython("SerialTimeout")) + @pyqtSlot(str) def lls(self, dirname): """ @@ -945,6 +1027,17 @@ except Exception as exc: self.error.emit("rmdir", str(exc)) + def fileSystemInfo(self): + """ + Public method to obtain information about the currently mounted file + systems. + """ + try: + fsinfo = self.__fs.fileSystemInfo() + self.fsinfoDone.emit(fsinfo) + except Exception as exc: + self.error.emit("fileSystemInfo", str(exc)) + ################################################################## ## some non-filesystem related methods below ################################################################## @@ -983,3 +1076,26 @@ self.showVersionDone.emit(versionInfo) except Exception as exc: self.error.emit("showVersion", str(exc)) + + @pyqtSlot() + def showImplementation(self): + """ + Public slot to obtain some implementation related information. + """ + try: + impInfo = self.__fs.getImplementation() + if impInfo["name"] == "micropython": + name = "MicroPython" + elif impInfo["name"] == "circuitpython": + name = "CircuitPython" + elif impInfo["name"] == "unknown": + name = self.tr("unknown") + else: + name = impInfo["name"] + if impInfo["version"] == "unknown": + version = self.tr("unknown") + else: + version = impInfo["version"] + self.showImplementationDone.emit(name, version) + except Exception as exc: + self.error.emit("showVersion", str(exc))