MicroPython: started to implement the file manager widget. micropython

Sun, 21 Jul 2019 19:54:15 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Sun, 21 Jul 2019 19:54:15 +0200
branch
micropython
changeset 7077
3b7475b7a1ef
parent 7070
3368ce0e7879
child 7078
bca506f8c756

MicroPython: started to implement the file manager widget.

eric6.e4p file | annotate | diff | comparison | revisions
eric6/MicroPython/EspDevices.py file | annotate | diff | comparison | revisions
eric6/MicroPython/MicroPythonFileSystem.py file | annotate | diff | comparison | revisions
eric6/MicroPython/MicroPythonReplWidget.py file | annotate | diff | comparison | revisions
eric6/MicroPython/MicroPythonSerialPort.py file | annotate | diff | comparison | revisions
--- a/eric6.e4p	Sat Jul 20 14:48:09 2019 +0200
+++ b/eric6.e4p	Sun Jul 21 19:54:15 2019 +0200
@@ -457,6 +457,7 @@
     <Source>eric6/MicroPython/CircuitPythonDevices.py</Source>
     <Source>eric6/MicroPython/EspDevices.py</Source>
     <Source>eric6/MicroPython/MicroPythonDevices.py</Source>
+    <Source>eric6/MicroPython/MicroPythonFileManagerWidget.py</Source>
     <Source>eric6/MicroPython/MicroPythonFileSystem.py</Source>
     <Source>eric6/MicroPython/MicroPythonGraphWidget.py</Source>
     <Source>eric6/MicroPython/MicroPythonReplWidget.py</Source>
@@ -1841,6 +1842,7 @@
     <Form>eric6/HexEdit/HexEditReplaceWidget.ui</Form>
     <Form>eric6/HexEdit/HexEditSearchWidget.ui</Form>
     <Form>eric6/IconEditor/IconSizeDialog.ui</Form>
+    <Form>eric6/MicroPython/MicroPythonFileManagerWidget.ui</Form>
     <Form>eric6/MicroPython/MicroPythonReplWidget.ui</Form>
     <Form>eric6/MultiProject/AddProjectDialog.ui</Form>
     <Form>eric6/MultiProject/PropertiesDialog.ui</Form>
@@ -2300,14 +2302,14 @@
     <Other>docs/THANKS</Other>
     <Other>docs/changelog</Other>
     <Other>eric6.e4p</Other>
+    <Other>eric6/APIs/Python/zope-2.10.7.api</Other>
+    <Other>eric6/APIs/Python/zope-2.11.2.api</Other>
+    <Other>eric6/APIs/Python/zope-3.3.1.api</Other>
     <Other>eric6/APIs/Python3/PyQt4.bas</Other>
     <Other>eric6/APIs/Python3/PyQt5.bas</Other>
     <Other>eric6/APIs/Python3/QScintilla2.bas</Other>
     <Other>eric6/APIs/Python3/eric6.api</Other>
     <Other>eric6/APIs/Python3/eric6.bas</Other>
-    <Other>eric6/APIs/Python/zope-2.10.7.api</Other>
-    <Other>eric6/APIs/Python/zope-2.11.2.api</Other>
-    <Other>eric6/APIs/Python/zope-3.3.1.api</Other>
     <Other>eric6/APIs/QSS/qss.api</Other>
     <Other>eric6/APIs/Ruby/Ruby-1.8.7.api</Other>
     <Other>eric6/APIs/Ruby/Ruby-1.8.7.bas</Other>
--- a/eric6/MicroPython/EspDevices.py	Sat Jul 20 14:48:09 2019 +0200
+++ b/eric6/MicroPython/EspDevices.py	Sun Jul 21 19:54:15 2019 +0200
@@ -127,7 +127,6 @@
         pythonScript = script.split("\n")
         self.sendCommands(pythonScript)
     
-    # TODO: not yet implemented
     def canStartFileManager(self):
         """
         Public method to determine, if a File Manager can be started.
@@ -136,9 +135,14 @@
             File Manager and a reason why it cannot.
         @rtype tuple of (bool, str)
         """
-        return False, self.tr("File Manager is not supported by this device.")
+        if self.__replActive or self.__plotterActive:
+            return False, self.tr("The file manager and the REPL/plotter use"
+                                  " the same USB serial connection. Only one"
+                                  " can be active at any time. Disconnect the"
+                                  " REPL/plotter and try again.")
+        else:
+            return True, ""
     
-    # TODO: not yet implemented
     def setFileManager(self, on):
         """
         Public method to set the File Manager status and dependent status.
@@ -146,7 +150,9 @@
         @param on flag indicating the active status
         @type bool
         """
-        pass
+        self.__fileManagerActive = on
+        self.microPython.setActionButtons(
+            run=not on, repl=not on, chart=HAS_QTCHART and not on)
     
     @pyqtSlot()
     def handleDataFlood(self):
--- a/eric6/MicroPython/MicroPythonFileSystem.py	Sat Jul 20 14:48:09 2019 +0200
+++ b/eric6/MicroPython/MicroPythonFileSystem.py	Sun Jul 21 19:54:15 2019 +0200
@@ -13,7 +13,9 @@
 import time
 import stat
 
-from PyQt5.QtCore import QObject, QThread
+from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject, QThread
+
+from .MicroPythonSerialPort import MicroPythonSerialPort
 
 
 class MicroPythonFileSystem(QObject):
@@ -328,60 +330,132 @@
         @exception IOError raised to indicate an issue with the device
         """
         # TODO: not implemented yet
+
+
+class MicroPythonFileManager(QObject):
+    """
+    Class implementing an interface to the device file system commands with
+    some additional sugar.
     
-    ##################################################################
-    ## Utility methods below
-    ##################################################################
+    @signal longListFiles(result) emitted with a tuple of tuples containing the
+        name, mode, size and time for each directory entry
+    @signal currentDir(dirname) emitted to report the current directory of the
+        device
     
-    def mtime2string(self, mtime):
+    @signal longListFilesFailed(exc) emitted with a failure message to indicate
+        a failed long listing operation
+    @signal currentDirFailed(exc) emitted with a failure message to indicate
+        that the current directory is not available
+    """
+    longListFiles = pyqtSignal(tuple)
+    currentDir = pyqtSignal(str)
+    
+    longListFilesFailed = pyqtSignal(str)
+    currentDirFailed = pyqtSignal(str)
+    
+    def __init__(self, port, parent=None):
         """
-        Public method to convert a time value to a string representation.
+        Constructor
         
-        @param mtime time value
-        @type int
-        @return string representation of the given time
-        @rtype str
+        @param port port name of the device
+        @type str
+        @param parent reference to the parent object
+        @type QObject
         """
-        return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(mtime))
+        super(MicroPythonFileManager, self).__init__(parent)
+        
+        self.__serialPort = port
+        self.__serial = MicroPythonSerialPort(parent=self)
+        self.__fs = MicroPythonFileSystem(parent=self)
     
-    def mode2string(self, mode):
+    @pyqtSlot()
+    def connect(self):
+        """
+        Public slot to start the manager.
         """
-        Public method to convert a mode value to a string representation.
+        self.__serial.openSerialLink(self.__serialPort)
+        self.__fs.setSerial(self.__serial)
+    
+    @pyqtSlot()
+    def disconnect(self):
+        """
+        Public slot to stop the thread.
+        """
+        self.__serial.closeSerialLink()
+    
+    @pyqtSlot(str)
+    def lls(self, dirname):
+        """
+        Public method to get a long listing of the given directory.
         
-        @param mode mode value
-        @type int
-        @return string representation of the given mode value
-        @rtype str
+        @param dirname name of the directory to list
+        @type str
         """
-        return stat.filemode(mode)
-# TODO: remove this
-##
-##if __name__ == "__main__":
-##    from PyQt5.QtCore import QCoreApplication, QTimer
-##    from MicroPythonSerialPort import MicroPythonSerialPort
-##    
-##    app = QCoreApplication([])
-##    
-##    serial = MicroPythonSerialPort()
-##    serial.openSerialLink("/dev/ttyUSB0")
-##    fs = MicroPythonFileSystem()
-##    fs.setSerial(serial)
-##    
-##    def tf():
-##        fs.cd("/flash")
-##        print(fs.pwd())
-##        fs.cd("odroid_go")
-##        print(fs.pwd())
-##        ll = fs.lls()
-##        print(ll)
-##        for f, (m, s, t) in ll:
-##            print(fs.mode2string(m), s, fs.mtime2string(t), f)
-##        fs.cd("..")
-##        print(fs.pwd())
-##        ll = fs.lls("odroid_go")
-##        print(ll)
-##        for f, (m, s, t) in ll:
-##            print(fs.mode2string(m), s, fs.mtime2string(t), f)
-##    
-##    QTimer.singleShot(0, tf)
-##    app.exec_()
+        try:
+            filesList = self.__fs.lls(dirname)
+            result = [(decoratedName(name, mode),
+                       mode2string(mode),
+                       str(size),
+                       mtime2string(time)) for
+                      name, (mode, size, time) in filesList]
+            self.longListFiles.emit(tuple(result))
+        except Exception as exc:
+            self.longListFilesFailed.emit(str(exc))
+    
+    @pyqtSlot()
+    def pwd(self):
+        """
+        Public method to get the current directory of the device.
+        """
+        try:
+            pwd = self.__fs.pwd()
+            self.currentDir.emit(pwd)
+        except Exception as exc:
+            self.currentDirFailed.emit(str(exc))
+
+##################################################################
+## Utility methods below
+##################################################################
+
+
+def mtime2string(mtime):
+    """
+    Function to convert a time value to a string representation.
+    
+    @param mtime time value
+    @type int
+    @return string representation of the given time
+    @rtype str
+    """
+    return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(mtime))
+
+
+def mode2string(mode):
+    """
+    Function to convert a mode value to a string representation.
+    
+    @param mode mode value
+    @type int
+    @return string representation of the given mode value
+    @rtype str
+    """
+    return stat.filemode(mode)
+
+
+def decoratedName(name, mode):
+    """
+    Function to decorate the given name according to the given mode.
+    
+    @param name file or directory name
+    @type str
+    @param mode mode value
+    @type int
+    @return decorated file or directory name
+    @rtype str
+    """
+    if stat.S_ISDIR(mode):
+        # append a '/' for directories
+        return name + "/"
+    else:
+        # no change
+        return name
--- a/eric6/MicroPython/MicroPythonReplWidget.py	Sat Jul 20 14:48:09 2019 +0200
+++ b/eric6/MicroPython/MicroPythonReplWidget.py	Sun Jul 21 19:54:15 2019 +0200
@@ -38,6 +38,7 @@
     HAS_QTCHART = True
 except ImportError:
     HAS_QTCHART = False
+from .MicroPythonFileManagerWidget import MicroPythonFileManagerWidget
 
 import Globals
 import UI.PixmapCache
@@ -878,9 +879,6 @@
             self.__showNoDeviceMessage()
             return
         
-        if self.__ui is None:
-            self.__ui = e5App().getObject("UserInterface")
-        
         if self.__plotterRunning:
             if self.__chartWidget.isDirty():
                 res = E5MessageBox.okToClearData(
@@ -939,3 +937,42 @@
         """
         self.on_disconnectButton_clicked()
         self.__device.handleDataFlood()
+    
+    @pyqtSlot()
+    def on_filesButton_clicked(self):
+        """
+        Private slot to open a file manager window to the connected device.
+        """
+        if not self.__device:
+            self.__showNoDeviceMessage()
+            return
+        
+        if self.__fileManagerRunning:
+            self.__fileManagerWidget.stop()
+            self.__ui.removeSideWidget(self.__fileManagerWidget)
+            
+            self.__device.setFileManager(False)
+            self.__fileManagerRunning = False
+        else:
+            ok, reason = self.__device.canStartFileManager()
+            if not ok:
+                E5MessageBox.warning(
+                    self,
+                    self.tr("Start File Manager"),
+                    self.tr("""<p>The File Manager cannot be started.</p>"""
+                            """<p>Reason: {0}</p>""").format(reason))
+                return
+            
+            port = self.__getCurrentPort()
+            self.__fileManagerWidget = MicroPythonFileManagerWidget(port, self)
+            
+            self.__ui.addSideWidget(self.__ui.BottomSide,
+                                    self.__fileManagerWidget,
+                                    UI.PixmapCache.getIcon("filemanager"),
+                                    self.tr("μPy Files"))
+            self.__ui.showSideWidget(self.__fileManagerWidget)
+
+            self.__device.setFileManager(True)
+            self.__fileManagerRunning = True
+            
+            self.__fileManagerWidget.start()
--- a/eric6/MicroPython/MicroPythonSerialPort.py	Sat Jul 20 14:48:09 2019 +0200
+++ b/eric6/MicroPython/MicroPythonSerialPort.py	Sun Jul 21 19:54:15 2019 +0200
@@ -10,7 +10,7 @@
 
 from __future__ import unicode_literals
 
-from PyQt5.QtCore import QIODevice, QTime
+from PyQt5.QtCore import QIODevice, QTime, QCoreApplication
 from PyQt5.QtSerialPort import QSerialPort
 
 
@@ -69,7 +69,7 @@
         """
         Public method to close the open serial connection.
         """
-        if self.__connceted:
+        if self.__connected:
             self.close()
             
             self.__connected = False
@@ -95,15 +95,12 @@
         @return bytes read from the device including the expected sequence
         @rtype bytes
         """
-        from PyQt5.QtCore import QCoreApplication, QEventLoop
         data = bytearray()
         
         t = QTime()
         t.start()
         while True:
-            # TODO: check if this is still needed when used with a QThread
-            QCoreApplication.processEvents(QEventLoop.ExcludeUserInputEvents)
-##            if self.waitForReadyRead(self.__timeout):
+            QCoreApplication.processEvents()
             c = bytes(self.read(1))
             if c:
                 data += c
@@ -115,7 +112,5 @@
 #                break
             if t.elapsed() > self.__timeout:
                 break
-##            else:
-##                break
         
         return bytes(data)

eric ide

mercurial