diff -r eab46b63ed91 -r 71883ddcb762 eric6/MicroPython/PyBoardDevices.py --- a/eric6/MicroPython/PyBoardDevices.py Sat Nov 02 18:19:24 2019 +0100 +++ b/eric6/MicroPython/PyBoardDevices.py Sat Nov 02 18:21:01 2019 +0100 @@ -7,8 +7,13 @@ Module implementing the device interface class for PyBoard boards. """ -from E5Gui import E5MessageBox +import os + +from PyQt5.QtCore import pyqtSlot, QStandardPaths + +from E5Gui import E5MessageBox, E5FileDialog from E5Gui.E5Application import e5App +from E5Gui.E5ProcessDialog import E5ProcessDialog from .MicroPythonDevices import MicroPythonDevice from .MicroPythonWidget import HAS_QTCHART @@ -183,15 +188,167 @@ @param menu reference to the context menu @type QMenu """ - menu.addAction( - self.tr("MicroPython Install Instructions"), - self.__showInstallInstructions) - # TODO: add entry to flash a new firmware using dfu-util + connected = self.microPython.isConnected() + + act = menu.addAction(self.tr("List DFU-capable Devices"), + self.__listDfuCapableDevices) + act.setEnabled(not connected) + act = menu.addAction(self.tr("Flash MicroPython Firmware"), + self.__flashMicroPython) + act.setEnabled(not connected) + menu.addSeparator() + menu.addAction(self.tr("MicroPython Flash Instructions"), + self.__showFlashInstructions) - def __showInstallInstructions(self): + @pyqtSlot() + def __showFlashInstructions(self): """ Private slot to open the URL containing instructions for installing MicroPython on the pyboard. """ e5App().getObject("UserInterface").launchHelpViewer( PyBoardDevice.FlashInstructionsURL) + + def __dfuUtilAvailable(self): + """ + Private method to check the availability of dfu-util. + + @return flag indicating the availability of dfu-util + @rtype bool + """ + available = False + program = Preferences.getMicroPython("DfuUtilPath") + if not program: + program = "dfu-util" + if Utilities.isinpath(program): + available = True + else: + if Utilities.isExecutable(program): + available = True + + if not available: + E5MessageBox.critical( + self.microPython, + self.tr("dfu-util not available"), + self.tr("""The dfu-util firmware flashing tool""" + """ <b>dfu-util</b> cannot be found or is not""" + """ executable. Ensure it is in the search path""" + """ or configure it on the MicroPython""" + """ configuration page.""") + ) + + return available + + def __showDfuEnableInstructions(self, flash=True): + """ + Private method to show some instructions to enable the DFU mode. + + @param flash flag indicating to show a warning message for flashing + @type bool + @return flag indicating OK to continue or abort + @rtype bool + """ + msg = self.tr( + "<h3>Enable DFU Mode</h3>" + "<p>1. Disconnect everything from your board</p>" + "<p>2. Disconnect your board</p>" + "<p>3. Connect the DFU/BOOT0 pin with a 3.3V pin</p>" + "<p>4. Re-connect your board</p>" + "<hr />" + ) + + if flash: + msg += self.tr( + "<p><b>Warning:</b> Make sure that all other DFU capable" + " devices except your PyBoard are disconnected." + "<hr />" + ) + + msg += self.tr( + "<p>Press <b>OK</b> to continue...</p>" + ) + res = E5MessageBox.information( + self.microPython, + self.tr("Enable DFU mode"), + msg, + E5MessageBox.StandardButtons( + E5MessageBox.Abort | + E5MessageBox.Ok)) + + return res == E5MessageBox.Ok + + def __showDfuDisableInstructions(self): + """ + Private method to show some instructions to disable the DFU mode. + """ + msg = self.tr( + "<h3>Disable DFU Mode</h3>" + "<p>1. Disconnect your board</p>" + "<p>2. Remove the DFU jumper</p>" + "<p>3. Re-connect your board</p>" + "<hr />" + "<p>Press <b>OK</b> to continue...</p>" + ) + E5MessageBox.information( + self.microPython, + self.tr("Disable DFU mode"), + msg + ) + + @pyqtSlot() + def __listDfuCapableDevices(self): + """ + Private slot to list all DFU-capable devices. + """ + if self.__dfuUtilAvailable(): + ok2continue = self.__showDfuEnableInstructions(flash=False) + if ok2continue: + program = Preferences.getMicroPython("DfuUtilPath") + if not program: + program = "dfu-util" + + args = [ + "--list", + ] + dlg = E5ProcessDialog( + self.tr("'dfu-util' Output"), + self.tr("List DFU capable Devices") + ) + res = dlg.startProcess(program, args) + if res: + dlg.exec_() + + @pyqtSlot() + def __flashMicroPython(self): + """ + Private slot to flash a MicroPython firmware. + """ + if self.__dfuUtilAvailable(): + ok2continue = self.__showDfuEnableInstructions() + if ok2continue: + program = Preferences.getMicroPython("DfuUtilPath") + if not program: + program = "dfu-util" + + downloadsPath = QStandardPaths.standardLocations( + QStandardPaths.DownloadLocation)[0] + firmware = E5FileDialog.getOpenFileName( + self.microPython, + self.tr("Flash MicroPython Firmware"), + downloadsPath, + self.tr( + "MicroPython Firmware Files (*.dfu);;All Files (*)") + ) + if firmware and os.path.exists(firmware): + args = [ + "--alt", "0", + "--download", firmware, + ] + dlg = E5ProcessDialog( + self.tr("'dfu-util' Output"), + self.tr("Flash MicroPython Firmware") + ) + res = dlg.startProcess(program, args) + if res: + dlg.exec_() + self.__showDfuDisableInstructions()