MicroPython: continued implementing the file manager widget. micropython

Sat, 27 Jul 2019 17:02:01 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Sat, 27 Jul 2019 17:02:01 +0200
branch
micropython
changeset 7088
e29b0ee86b29
parent 7087
2ca7fb61a82f
child 7089
9f9816b19aa4

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))

eric ide

mercurial