microbit: added the minimal filesystem commands which are supported by the BBC micro:bit. micropython

Wed, 07 Aug 2019 16:10:12 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Wed, 07 Aug 2019 16:10:12 +0200
branch
micropython
changeset 7126
376deb7fefe7
parent 7125
2028553ee58c
child 7127
aa6fc2d252ad

microbit: added the minimal filesystem commands which are supported by the BBC micro:bit.

eric6/MicroPython/MicroPythonCommandsInterface.py file | annotate | diff | comparison | revisions
eric6/MicroPython/MicroPythonFileManagerWidget.py file | annotate | diff | comparison | revisions
eric6/MicroPython/MicroPythonFileManagerWidget.ui file | annotate | diff | comparison | revisions
eric6/MicroPython/MicroPythonReplWidget.py file | annotate | diff | comparison | revisions
eric6/MicroPython/MicrobitDevices.py file | annotate | diff | comparison | revisions
eric6/icons/default/getAs.png file | annotate | diff | comparison | revisions
eric6/icons/default/putAs.png file | annotate | diff | comparison | revisions
--- a/eric6/MicroPython/MicroPythonCommandsInterface.py	Wed Aug 07 16:08:46 2019 +0200
+++ b/eric6/MicroPython/MicroPythonCommandsInterface.py	Wed Aug 07 16:10:12 2019 +0200
@@ -67,6 +67,8 @@
         """
         super(MicroPythonCommandsInterface, self).__init__(parent)
         
+        self.__repl = parent
+        
         self.__blockReadyRead = False
         
         self.__serial = MicroPythonSerialPort(
@@ -291,11 +293,19 @@
         @rtype tuple of str
         @exception IOError raised to indicate an issue with the device
         """
-        commands = [
-            "import os as __os_",
-            "print(__os_.listdir('{0}'))".format(dirname),
-            "del __os_",
-        ]
+        if self.__repl.isMicrobit():
+            # BBC micro:bit does not support directories
+            commands = [
+                "import os as __os_",
+                "print(__os_.listdir())",
+                "del __os_",
+            ]
+        else:
+            commands = [
+                "import os as __os_",
+                "print(__os_.listdir('{0}'))".format(dirname),
+                "del __os_",
+            ]
         out, err = self.execute(commands)
         if err:
             raise IOError(self.__shortError(err))
@@ -317,35 +327,58 @@
         @rtype tuple of (str, tuple)
         @exception IOError raised to indicate an issue with the device
         """
-        commands = [
-            "import os as __os_",
-            "\n".join([
-                "def is_visible(filename):",
-                "    return filename[0] != '.' and filename[-1] != '~'",
-            ]),
-            "\n".join([
-                "def stat(filename):",
-                "    try:",
-                "        rstat = __os_.lstat(filename)",
-                "    except:",
-                "        rstat = __os_.stat(filename)",
-                "    return tuple(rstat)",
-            ]),
-            "\n".join([
-                "def listdir_stat(dirname):",
-                "    try:",
-                "        files = __os_.listdir(dirname)",
-                "    except OSError:",
-                "        return None",
-                "    if dirname in ('', '/'):",
-                "        return list((f, stat(f)) for f in files if"
-                " is_visible(f))",
-                "    return list((f, stat(dirname + '/' + f)) for f in files"
-                " if is_visible(f))",
-            ]),
-            "print(listdir_stat('{0}'))".format(dirname),
-            "del __os_, stat, listdir_stat",
-        ]
+        if self.__repl.isMicrobit():
+            # BBC micro:bit does not support directories
+            commands = [
+                "import os as __os_",
+                "\n".join([
+                    "def is_visible(filename):",
+                    "    return filename[0] != '.' and filename[-1] != '~'",
+                ]),
+                "\n".join([
+                    "def stat(filename):",
+                    "    size = __os_.size(filename)",
+                    "    return (0, 0, 0, 0, 0, 0, size, 0, 0, 0)"
+                ]),
+                "\n".join([
+                    "def listdir_stat():",
+                    "    files = __os_.listdir()",
+                    "    return list((f, stat(f)) for f in files if"
+                    " is_visible(f))",
+                ]),
+                "print(listdir_stat())",
+                "del __os_, stat, listdir_stat, is_visible",
+            ]
+        else:
+            commands = [
+                "import os as __os_",
+                "\n".join([
+                    "def is_visible(filename):",
+                    "    return filename[0] != '.' and filename[-1] != '~'",
+                ]),
+                "\n".join([
+                    "def stat(filename):",
+                    "    try:",
+                    "        rstat = __os_.lstat(filename)",
+                    "    except:",
+                    "        rstat = __os_.stat(filename)",
+                    "    return tuple(rstat)",
+                ]),
+                "\n".join([
+                    "def listdir_stat(dirname):",
+                    "    try:",
+                    "        files = __os_.listdir(dirname)",
+                    "    except OSError:",
+                    "        return None",
+                    "    if dirname in ('', '/'):",
+                    "        return list((f, stat(f)) for f in files if"
+                    " is_visible(f))",
+                    "    return list((f, stat(dirname + '/' + f))"
+                    " for f in files if is_visible(f))",
+                ]),
+                "print(listdir_stat('{0}'))".format(dirname),
+                "del __os_, stat, listdir_stat, is_visible",
+            ]
         out, err = self.execute(commands)
         if err:
             raise IOError(self.__shortError(err))
@@ -385,6 +418,10 @@
         @rtype str
         @exception IOError raised to indicate an issue with the device
         """
+        if self.__repl.isMicrobit():
+            # BBC micro:bit does not support directories
+            return ""
+        
         commands = [
             "import os as __os_",
             "print(__os_.getcwd())",
--- a/eric6/MicroPython/MicroPythonFileManagerWidget.py	Wed Aug 07 16:08:46 2019 +0200
+++ b/eric6/MicroPython/MicroPythonFileManagerWidget.py	Wed Aug 07 16:10:12 2019 +0200
@@ -55,16 +55,24 @@
         super(MicroPythonFileManagerWidget, self).__init__(parent)
         self.setupUi(self)
         
+        self.__repl = parent
+        
         self.syncButton.setIcon(UI.PixmapCache.getIcon("2rightarrow"))
         self.putButton.setIcon(UI.PixmapCache.getIcon("1rightarrow"))
+        self.putAsButton.setIcon(UI.PixmapCache.getIcon("putAs"))
         self.getButton.setIcon(UI.PixmapCache.getIcon("1leftarrow"))
+        self.getAsButton.setIcon(UI.PixmapCache.getIcon("getAs"))
         self.localUpButton.setIcon(UI.PixmapCache.getIcon("1uparrow"))
         self.localReloadButton.setIcon(UI.PixmapCache.getIcon("reload"))
         self.deviceUpButton.setIcon(UI.PixmapCache.getIcon("1uparrow"))
         self.deviceReloadButton.setIcon(UI.PixmapCache.getIcon("reload"))
         
+        self.deviceUpButton.setEnabled(not self.__repl.isMicrobit())
+        
         self.putButton.setEnabled(False)
+        self.putAsButton.setEnabled(False)
         self.getButton.setEnabled(False)
+        self.getAsButton.setEnabled(False)
         
         self.localFileTreeWidget.header().setSortIndicator(
             0, Qt.AscendingOrder)
@@ -109,20 +117,23 @@
             self.tr("Show Time"), self.__showLocalTime)
         
         self.__deviceMenu = QMenu(self)
-        self.__deviceMenu.addAction(
-            self.tr("Change Directory"), self.__changeDeviceDirectory)
-        self.__deviceMenu.addAction(
-            self.tr("Create Directory"), self.__createDeviceDirectory)
-        self.__devDelDirAct = self.__deviceMenu.addAction(
-            self.tr("Delete Directory"), self.__deleteDeviceDirectory)
-        self.__devDelDirTreeAct = self.__deviceMenu.addAction(
-            self.tr("Delete Directory Tree"), self.__deleteDeviceDirectoryTree)
-        self.__deviceMenu.addSeparator()
+        if not self.__repl.isMicrobit():
+            self.__deviceMenu.addAction(
+                self.tr("Change Directory"), self.__changeDeviceDirectory)
+            self.__deviceMenu.addAction(
+                self.tr("Create Directory"), self.__createDeviceDirectory)
+            self.__devDelDirAct = self.__deviceMenu.addAction(
+                self.tr("Delete Directory"), self.__deleteDeviceDirectory)
+            self.__devDelDirTreeAct = self.__deviceMenu.addAction(
+                self.tr("Delete Directory Tree"),
+                self.__deleteDeviceDirectoryTree)
+            self.__deviceMenu.addSeparator()
         self.__devDelFileAct = self.__deviceMenu.addAction(
             self.tr("Delete File"), self.__deleteDeviceFile)
-        self.__deviceMenu.addSeparator()
-        self.__deviceMenu.addAction(
-            self.tr("Show Filesystem Info"), self.__showFileSystemInfo)
+        if not parent.isMicrobit():
+            self.__deviceMenu.addSeparator()
+            self.__deviceMenu.addAction(
+                self.tr("Show Filesystem Info"), self.__showFileSystemInfo)
     
     def start(self):
         """
@@ -251,6 +262,7 @@
                 self.localFileTreeWidget.selectedItems()[0].text(0)
                 .endswith("/"))
         self.putButton.setEnabled(enable)
+        self.putAsButton.setEnabled(enable)
     
     @pyqtSlot()
     def on_localUpButton_clicked(self):
@@ -298,6 +310,7 @@
                 self.deviceFileTreeWidget.selectedItems()[0].text(0)
                 .endswith("/"))
         self.getButton.setEnabled(enable)
+        self.getAsButton.setEnabled(enable)
     
     @pyqtSlot()
     def on_deviceUpButton_clicked(self):
@@ -338,19 +351,35 @@
         return False
     
     @pyqtSlot()
-    def on_putButton_clicked(self):
+    def on_putButton_clicked(self, putAs=False):
         """
         Private slot to copy the selected file to the connected device.
+        
+        @param putAs flag indicating to give it a new name
+        @type bool
         """
         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):
+                if putAs:
+                    deviceFilename, ok = QInputDialog.getText(
+                        self,
+                        self.tr("Put File As"),
+                        self.tr("Enter a new name for the file"),
+                        QLineEdit.Normal,
+                        filename)
+                    if not ok or not filename:
+                        return
+                else:
+                    deviceFilename = filename
+                
+                if self.__isFileInList(deviceFilename,
+                                       self.deviceFileTreeWidget):
                     # ask for overwrite permission
                     action, resultFilename = confirmOverwrite(
-                        filename, self.tr("Copy File to Device"),
+                        deviceFilename, self.tr("Copy File to Device"),
                         self.tr("The given file exists already"
                                 " (Enter file name only)."),
                         False, self)
@@ -358,49 +387,79 @@
                         return
                     elif action == "rename":
                         deviceFilename = os.path.basename(resultFilename)
-                    else:
-                        deviceFilename = filename
-                else:
-                    deviceFilename = filename
                 
+                deviceCwd = self.deviceCwd.text()
+                if deviceCwd:
+                    deviceFilename = deviceCwd + "/" + deviceFilename
                 self.__fileManager.put(
                     os.path.join(self.localCwd.text(), filename),
-                    os.path.join(self.deviceCwd.text(), deviceFilename)
+                    deviceFilename
                 )
     
     @pyqtSlot()
-    def on_getButton_clicked(self):
+    def on_putAsButton_clicked(self):
+        """
+        Private slot to copy the selected file to the connected device
+        with a different name.
+        """
+        self.on_putButton_clicked(putAs=True)
+    
+    @pyqtSlot()
+    def on_getButton_clicked(self, getAs=False):
         """
         Private slot to copy the selected file from the connected device.
+        
+        @param getAs flag indicating to give it a new name
+        @type bool
         """
         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):
+                if getAs:
+                    localFilename, ok = QInputDialog.getText(
+                        self,
+                        self.tr("Get File As"),
+                        self.tr("Enter a new name for the file"),
+                        QLineEdit.Normal,
+                        filename)
+                    if not ok or not filename:
+                        return
+                else:
+                    localFilename = filename
+                
+                if self.__isFileInList(localFilename,
+                                       self.localFileTreeWidget):
                     # ask for overwrite permission
                     action, resultFilename = confirmOverwrite(
-                        filename, self.tr("Copy File from Device"),
+                        localFilename, self.tr("Copy File from Device"),
                         self.tr("The given file exists already."),
                         True, self)
                     if action == "cancel":
                         return
                     elif action == "rename":
                         localFilename = resultFilename
-                    else:
-                        localFilename = filename
-                else:
-                    localFilename = filename
                 
+                deviceCwd = self.deviceCwd.text()
+                if deviceCwd:
+                    filename = deviceCwd + "/" + filename
                 if not os.path.isabs(localFilename):
                     localFilename = os.path.join(self.localCwd.text(),
                                                  localFilename)
                 self.__fileManager.get(
-                    os.path.join(self.deviceCwd.text(), filename),
+                    filename,
                     localFilename
                 )
     
+    @pyqtSlot()
+    def on_getAsButton_clicked(self):
+        """
+        Private slot to copy the selected file from the connected device
+        with a different name.
+        """
+        self.on_getButton_clicked(getAs=True)
+    
     @pyqtSlot(str, str)
     def __handleGetDone(self, deviceFile, localFile):
         """
@@ -630,8 +689,9 @@
         else:
             isDir = False
             isFile = False
-        self.__devDelDirAct.setEnabled(isDir)
-        self.__devDelDirTreeAct.setEnabled(isDir)
+        if not self.__repl.isMicrobit():
+            self.__devDelDirAct.setEnabled(isDir)
+            self.__devDelDirTreeAct.setEnabled(isDir)
         self.__devDelFileAct.setEnabled(isFile)
         
         self.__deviceMenu.exec_(self.deviceFileTreeWidget.mapToGlobal(pos))
@@ -710,7 +770,11 @@
         """
         if bool(len(self.deviceFileTreeWidget.selectedItems())):
             name = self.deviceFileTreeWidget.selectedItems()[0].text(0)
-            filename = self.deviceCwd.text() + "/" + name
+            dirname = self.deviceCwd.text()
+            if dirname:
+                filename = dirname + "/" + name
+            else:
+                filename = name
             dlg = DeleteFilesConfirmationDialog(
                 self,
                 self.tr("Delete File"),
--- a/eric6/MicroPython/MicroPythonFileManagerWidget.ui	Wed Aug 07 16:08:46 2019 +0200
+++ b/eric6/MicroPython/MicroPythonFileManagerWidget.ui	Wed Aug 07 16:10:12 2019 +0200
@@ -96,7 +96,7 @@
        <property name="sizeHint" stdset="0">
         <size>
          <width>20</width>
-         <height>40</height>
+         <height>13</height>
         </size>
        </property>
       </spacer>
@@ -116,6 +116,13 @@
       </widget>
      </item>
      <item>
+      <widget class="QToolButton" name="putAsButton">
+       <property name="toolTip">
+        <string>Press to copy the selected file to the device with a new name</string>
+       </property>
+      </widget>
+     </item>
+     <item>
       <widget class="QToolButton" name="getButton">
        <property name="toolTip">
         <string>Press to copy the selected file from the device</string>
@@ -123,6 +130,13 @@
       </widget>
      </item>
      <item>
+      <widget class="QToolButton" name="getAsButton">
+       <property name="toolTip">
+        <string>Press to copy the selected file from the device with a new name</string>
+       </property>
+      </widget>
+     </item>
+     <item>
       <spacer name="verticalSpacer">
        <property name="orientation">
         <enum>Qt::Vertical</enum>
@@ -256,7 +270,9 @@
   <tabstop>deviceFileTreeWidget</tabstop>
   <tabstop>syncButton</tabstop>
   <tabstop>putButton</tabstop>
+  <tabstop>putAsButton</tabstop>
   <tabstop>getButton</tabstop>
+  <tabstop>getAsButton</tabstop>
   <tabstop>localUpButton</tabstop>
   <tabstop>localReloadButton</tabstop>
   <tabstop>deviceUpButton</tabstop>
--- a/eric6/MicroPython/MicroPythonReplWidget.py	Wed Aug 07 16:08:46 2019 +0200
+++ b/eric6/MicroPython/MicroPythonReplWidget.py	Wed Aug 07 16:10:12 2019 +0200
@@ -307,6 +307,19 @@
         """
         return self.__interface
     
+    def isMicrobit(self):
+        """
+        Public method to check, if the connected/selected device is a
+        BBC micro:bit.
+        
+        @return flag indicating a micro:bit device
+        rtype bool
+        """
+        if self.__device and "micro:bit" in self.__device.deviceName():
+            return True
+        
+        return False
+    
     @pyqtSlot(int)
     def on_deviceTypeComboBox_activated(self, index):
         """
--- a/eric6/MicroPython/MicrobitDevices.py	Wed Aug 07 16:08:46 2019 +0200
+++ b/eric6/MicroPython/MicrobitDevices.py	Wed Aug 07 16:10:12 2019 +0200
@@ -45,7 +45,7 @@
         """
         super(MicrobitDevice, self).setButtons()
         self.microPython.setActionButtons(
-            repl=True, chart=HAS_QTCHART)
+            run=True, repl=True, files=True, chart=HAS_QTCHART)
     
     def forceInterrupt(self):
         """
@@ -57,6 +57,15 @@
         """
         return True
     
+    def deviceName(self):
+        """
+        Public method to get the name of the device.
+        
+        @return name of the device
+        @rtype str
+        """
+        return self.tr("BBC micro:bit")
+    
     def canStartRepl(self):
         """
         Public method to determine, if a REPL can be started.
@@ -77,6 +86,26 @@
         """
         return True, ""
     
+    def canRunScript(self):
+        """
+        Public method to determine, if a script can be executed.
+        
+        @return tuple containing a flag indicating it is safe to start a
+            Plotter and a reason why it cannot.
+        @rtype tuple of (bool, str)
+        """
+        return True, ""
+    
+    def runScript(self, script):
+        """
+        Public method to run the given Python script.
+        
+        @param script script to be executed
+        @type str
+        """
+        pythonScript = script.split("\n")
+        self.sendCommands(pythonScript)
+    
     def canStartFileManager(self):
         """
         Public method to determine, if a File Manager can be started.
@@ -85,7 +114,7 @@
             File Manager and a reason why it cannot.
         @rtype tuple of (bool, str)
         """
-        return False, ""
+        return True, ""
     
     def getWorkspace(self):
         """
Binary file eric6/icons/default/getAs.png has changed
Binary file eric6/icons/default/putAs.png has changed

eric ide

mercurial