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