eric6/MicroPython/MicrobitDevices.py

branch
micropython
changeset 7123
94948e2aa0a5
parent 7114
f416440c8be1
child 7126
376deb7fefe7
--- a/eric6/MicroPython/MicrobitDevices.py	Tue Aug 06 16:42:39 2019 +0200
+++ b/eric6/MicroPython/MicrobitDevices.py	Tue Aug 06 17:45:38 2019 +0200
@@ -9,9 +9,20 @@
 
 from __future__ import unicode_literals
 
+import sys
+import os
+
+from PyQt5.QtCore import pyqtSlot, QStandardPaths
+
 from .MicroPythonDevices import MicroPythonDevice
 from .MicroPythonReplWidget import HAS_QTCHART
 
+from E5Gui import E5MessageBox, E5FileDialog
+from E5Gui.E5Application import e5App
+from E5Gui.E5ProcessDialog import E5ProcessDialog
+
+import Utilities
+
 
 class MicrobitDevice(MicroPythonDevice):
     """
@@ -34,7 +45,7 @@
         """
         super(MicrobitDevice, self).setButtons()
         self.microPython.setActionButtons(
-            repl=True, files=True, chart=HAS_QTCHART)
+            repl=True, chart=HAS_QTCHART)
     
     def forceInterrupt(self):
         """
@@ -74,4 +85,216 @@
             File Manager and a reason why it cannot.
         @rtype tuple of (bool, str)
         """
-        return True, ""
+        return False, ""
+    
+    def getWorkspace(self):
+        """
+        Public method to get the workspace directory.
+        
+        @return workspace directory used for saving files
+        @rtype str
+        """
+        # Attempts to find the path on the filesystem that represents the
+        # plugged in MICROBIT board.
+        deviceDirectory = Utilities.findVolume("MICROBIT")
+        
+        if deviceDirectory:
+            return deviceDirectory
+        else:
+            # return the default workspace and give the user a warning
+            E5MessageBox.warning(
+                self.microPython,
+                self.tr("Workspace Directory"),
+                self.tr("Could not find an attached BBC micro:bit.\n\n"
+                        "Please make sure the device is plugged "
+                        "into this computer."))
+            
+            return super(MicrobitDevice, self).getWorkspace()
+    
+    def hasTimeCommands(self):
+        """
+        Public method to check, if the device supports time commands.
+        
+        The default returns True.
+        
+        @return flag indicating support for time commands
+        @rtype bool
+        """
+        return False
+    
+    def addDeviceMenuEntries(self, menu):
+        """
+        Public method to add device specific entries to the given menu.
+        
+        @param menu reference to the context menu
+        @type QMenu
+        """
+        connected = self.microPython.isConnected()
+        
+        act = menu.addAction(self.tr("Flash Default MicroPython Firmware"),
+                             self.__flashMicroPython)
+        act.setEnabled(not connected)
+        act = menu.addAction(self.tr("Flash Custom MicroPython Firmware"),
+                             self.__flashCustomMicroPython)
+        act.setEnabled(not connected)
+        menu.addSeparator()
+        act = menu.addAction(self.tr("Flash Script"), self.__flashScript)
+        act.setToolTip(self.tr(
+            "Flash the current script to the selected device."))
+        act.setEnabled(not connected)
+        act = menu.addAction(self.tr("Save Script as 'main.py'"),
+                             self.__saveMain)
+        act.setToolTip(self.tr(
+            "Save the current script as 'main.py' on the connected device"))
+        act.setEnabled(connected)
+        menu.addSeparator()
+        act = menu.addAction(self.tr("Reset micro:bit"), self.__resetDevice)
+        act.setEnabled(connected)
+        menu.addSeparator()
+        menu.addAction(self.tr("Install 'uflash'"), self.__installUflashTool)
+    
+    @pyqtSlot()
+    def __flashMicroPython(self):
+        """
+        Private slot to flash the default MicroPython firmware to the device.
+        """
+        flashArgs = [
+            "-u",
+            "-m", "uflash",
+        ]
+        dlg = E5ProcessDialog(self.tr("'uflash' Output"),
+                              self.tr("Flash Default MicroPython Firmware"))
+        res = dlg.startProcess(sys.executable, flashArgs)
+        if res:
+            dlg.exec_()
+    
+    @pyqtSlot()
+    def __flashCustomMicroPython(self):
+        """
+        Private slot to flash a custom MicroPython firmware to the device.
+        """
+        downloadsPath = QStandardPaths.standardLocations(
+            QStandardPaths.DownloadLocation)[0]
+        firmware = E5FileDialog.getOpenFileName(
+            None,
+            self.tr("Flash Custom MicroPython Firmware"),
+            downloadsPath,
+            self.tr("MicroPython Firmware Files (*.hex);;All Files (*)"))
+        if firmware and os.path.exists(firmware):
+            flashArgs = [
+                "-u",
+                "-m", "uflash",
+                "--runtime", firmware,
+            ]
+            dlg = E5ProcessDialog(
+                self.tr("'uflash' Output"),
+                self.tr("Flash Default MicroPython Firmware"))
+            res = dlg.startProcess(sys.executable, flashArgs)
+            if res:
+                dlg.exec_()
+    
+    @pyqtSlot()
+    def __flashScript(self):
+        """
+        Private slot to flash the current script onto the selected device.
+        """
+        aw = e5App().getObject("ViewManager").activeWindow()
+        if not aw:
+            return
+        
+        if not (aw.isPy3File() or aw.isPy2File()):
+            yes = E5MessageBox.yesNo(
+                None,
+                self.tr("Flash Script"),
+                self.tr("""The current editor does not contain a Python"""
+                        """ script. Flash it anyway?"""))
+            if not yes:
+                return
+        
+        script = aw.text().strip()
+        if not script:
+            E5MessageBox.warning(
+                self,
+                self.tr("Flash Script"),
+                self.tr("""The script is empty. Aborting."""))
+            return
+        
+        if aw.checkDirty():
+            filename = aw.getFileName()
+            flashArgs = [
+                "-u",
+                "-m", "uflash",
+                filename,
+            ]
+            dlg = E5ProcessDialog(self.tr("'uflash' Output"),
+                                  self.tr("Flash Script"))
+            res = dlg.startProcess(sys.executable, flashArgs)
+            if res:
+                dlg.exec_()
+    
+    @pyqtSlot()
+    def __saveMain(self):
+        """
+        Private slot to copy the current script as 'main.py' onto the
+        connected device.
+        """
+        aw = e5App().getObject("ViewManager").activeWindow()
+        if not aw:
+            return
+        
+        if not (aw.isPy3File() or aw.isPy2File()):
+            yes = E5MessageBox.yesNo(
+                None,
+                self.tr("Save Script as 'main.py'"),
+                self.tr("""The current editor does not contain a Python"""
+                        """ script. Write it anyway?"""))
+            if not yes:
+                return
+        
+        script = aw.text().strip()
+        if not script:
+            E5MessageBox.warning(
+                self,
+                self.tr("Save Script as 'main.py'"),
+                self.tr("""The script is empty. Aborting."""))
+            return
+        
+        commands = [
+            "fd = open('main.py', 'wb')",
+            "f = fd.write",
+        ]
+        for line in script.splitlines():
+            commands.append("f(" + repr(line + "\n") + ")")
+        commands.append("fd.close()")
+        out, err = self.microPython.commandsInterface().execute(commands)
+        if err:
+            E5MessageBox.critical(
+                self,
+                self.tr("Save Script as 'main.py'"),
+                self.tr("""<p>The script could not be saved to the"""
+                        """ device.</p><p>Reason: {0}</p>""")
+                .format(err.decode("utf-8")))
+        
+        # reset the device
+        self.microPython.commandsInterface().execute([
+            "import microbit",
+            "microbit.reset()",
+        ])
+    
+    @pyqtSlot()
+    def __resetDevice(self):
+        """
+        Private slot to reset the connected device.
+        """
+        self.microPython.commandsInterface().execute([
+            "import microbit",
+            "microbit.reset()",
+        ])
+    
+    @pyqtSlot()
+    def __installUflashTool(self):
+        """
+        Private slot to install the uflash package via pip.
+        """
+        pip = e5App().getObject("Pip")
+        pip.installPackages(["uflash"], interpreter=sys.executable)

eric ide

mercurial