Started rearranging menu structure and testing and fixing on CircuitPython. micropython

Wed, 31 Jul 2019 20:41:39 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Wed, 31 Jul 2019 20:41:39 +0200
branch
micropython
changeset 7108
4f6133a01c6a
parent 7103
aea236dc8002
child 7111
62191d1aeeed

Started rearranging menu structure and testing and fixing on CircuitPython.

eric6.e4p file | annotate | diff | comparison | revisions
eric6/E5Gui/E5ProcessDialog.py file | annotate | diff | comparison | revisions
eric6/E5Gui/E5ProcessDialog.ui file | annotate | diff | comparison | revisions
eric6/MicroPython/EspDevices.py file | annotate | diff | comparison | revisions
eric6/MicroPython/EspFirmwareSelectionDialog.py file | annotate | diff | comparison | revisions
eric6/MicroPython/EspFirmwareSelectionDialog.ui file | annotate | diff | comparison | revisions
eric6/MicroPython/MicroPythonCommandsInterface.py file | annotate | diff | comparison | revisions
eric6/MicroPython/MicroPythonDevices.py file | annotate | diff | comparison | revisions
eric6/MicroPython/MicroPythonFileManager.py file | annotate | diff | comparison | revisions
eric6/MicroPython/MicroPythonFileManagerWidget.py file | annotate | diff | comparison | revisions
eric6/MicroPython/MicroPythonReplWidget.py file | annotate | diff | comparison | revisions
eric6/MicroPython/MicroPythonReplWidget.ui file | annotate | diff | comparison | revisions
eric6/Preferences/__init__.py file | annotate | diff | comparison | revisions
--- a/eric6.e4p	Tue Jul 30 19:44:27 2019 +0200
+++ b/eric6.e4p	Wed Jul 31 20:41:39 2019 +0200
@@ -145,6 +145,7 @@
     <Source>eric6/E5Gui/E5PasswordMeter.py</Source>
     <Source>eric6/E5Gui/E5PathPicker.py</Source>
     <Source>eric6/E5Gui/E5PathPickerDialog.py</Source>
+    <Source>eric6/E5Gui/E5ProcessDialog.py</Source>
     <Source>eric6/E5Gui/E5ProgressDialog.py</Source>
     <Source>eric6/E5Gui/E5SideBar.py</Source>
     <Source>eric6/E5Gui/E5SimpleHelpDialog.py</Source>
@@ -457,6 +458,7 @@
     <Source>eric6/IconEditor/cursors/cursors_rc.py</Source>
     <Source>eric6/MicroPython/CircuitPythonDevices.py</Source>
     <Source>eric6/MicroPython/EspDevices.py</Source>
+    <Source>eric6/MicroPython/EspFirmwareSelectionDialog.py</Source>
     <Source>eric6/MicroPython/MicroPythonCommandsInterface.py</Source>
     <Source>eric6/MicroPython/MicroPythonDevices.py</Source>
     <Source>eric6/MicroPython/MicroPythonFileManager.py</Source>
@@ -1777,6 +1779,7 @@
     <Form>eric6/Debugger/VariablesFilterDialog.ui</Form>
     <Form>eric6/E5Gui/E5ErrorMessageFilterDialog.ui</Form>
     <Form>eric6/E5Gui/E5ListSelectionDialog.ui</Form>
+    <Form>eric6/E5Gui/E5ProcessDialog.ui</Form>
     <Form>eric6/E5Gui/E5SimpleHelpDialog.ui</Form>
     <Form>eric6/E5Gui/E5StringListEditWidget.ui</Form>
     <Form>eric6/E5Gui/E5ToolBarDialog.ui</Form>
@@ -1847,6 +1850,7 @@
     <Form>eric6/HexEdit/HexEditReplaceWidget.ui</Form>
     <Form>eric6/HexEdit/HexEditSearchWidget.ui</Form>
     <Form>eric6/IconEditor/IconSizeDialog.ui</Form>
+    <Form>eric6/MicroPython/EspFirmwareSelectionDialog.ui</Form>
     <Form>eric6/MicroPython/MicroPythonFileManagerWidget.ui</Form>
     <Form>eric6/MicroPython/MicroPythonProgressInfoDialog.ui</Form>
     <Form>eric6/MicroPython/MicroPythonReplWidget.ui</Form>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eric6/E5Gui/E5ProcessDialog.py	Wed Jul 31 20:41:39 2019 +0200
@@ -0,0 +1,284 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2019 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog starting a process and showing its output.
+"""
+
+from __future__ import unicode_literals
+try:
+    str = unicode
+except NameError:
+    pass
+
+import os
+
+from PyQt5.QtCore import QProcess, QTimer, pyqtSlot, Qt, QCoreApplication, \
+    QProcessEnvironment
+from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QLineEdit
+
+from E5Gui import E5MessageBox
+
+from .Ui_E5ProcessDialog import Ui_E5ProcessDialog
+
+from Globals import strToQByteArray
+import Preferences
+
+
+class E5ProcessDialog(QDialog, Ui_E5ProcessDialog):
+    """
+    Class implementing a dialog starting a process and showing its output.
+    
+    It starts a QProcess and displays a dialog that
+    shows the output of the process. The dialog is modal,
+    which causes a synchronized execution of the process.
+    """
+    def __init__(self, outputTitle="", windowTitle="", parent=None):
+        """
+        Constructor
+        
+        @param outputTitle title for the output group
+        @type str
+        @param windowTitle title of the dialog
+        @type str
+        @param parent reference to the parent widget
+        @type QWidget
+        """
+        super(E5ProcessDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False)
+        self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True)
+        
+        if windowTitle:
+            self.setWindowTitle(windowTitle)
+        if outputTitle:
+            self.outputGroup.setTitle(outputTitle)
+        
+        self.__process = None
+        
+        self.show()
+        QCoreApplication.processEvents()
+    
+    def __finish(self):
+        """
+        Private slot called when the process finished or the user pressed
+        the button.
+        """
+        if self.__process is not None and \
+           self.__process.state() != QProcess.NotRunning:
+            self.__process.terminate()
+            QTimer.singleShot(2000, self.__process.kill)
+            self.__process.waitForFinished(3000)
+        
+        self.inputGroup.setEnabled(False)
+        self.inputGroup.hide()
+        
+        self.__process = None
+        
+        self.buttonBox.button(QDialogButtonBox.Close).setEnabled(True)
+        self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(False)
+        self.buttonBox.button(QDialogButtonBox.Close).setDefault(True)
+        self.buttonBox.button(QDialogButtonBox.Close).setFocus(
+            Qt.OtherFocusReason)
+    
+    def on_buttonBox_clicked(self, button):
+        """
+        Private slot called by a button of the button box clicked.
+        
+        @param button button that was clicked
+        @type QAbstractButton
+        """
+        if button == self.buttonBox.button(QDialogButtonBox.Close):
+            self.close()
+        elif button == self.buttonBox.button(QDialogButtonBox.Cancel):
+            self.statusLabel.setText(self.tr("Process canceled."))
+            self.__finish()
+    
+    def __procFinished(self, exitCode, exitStatus):
+        """
+        Private slot connected to the finished signal.
+        
+        @param exitCode exit code of the process
+        @type int
+        @param exitStatus exit status of the process
+        @type QProcess.ExitStatus
+        """
+        self.__normal = (exitStatus == QProcess.NormalExit) and (exitCode == 0)
+        if self.__normal:
+            self.statusLabel.setText(self.tr("Process finished successfully."))
+        elif exitStatus == QProcess.CrashExit:
+            self.statusLabel.setText(self.tr("Process crashed."))
+        else:
+            self.statusLabel.setText(
+                self.tr("Process finished with exit code {0}")
+                .format(exitCode))
+        self.__finish()
+    
+    def startProcess(self, program, args, workingDir=None, showArgs=True,
+                     environment=None):
+        """
+        Public slot used to start the process.
+        
+        @param program path of the program to be executed
+        @type str
+        @param args list of arguments for the process
+        @type list of str
+        @param workingDir working directory for the process
+        @type str
+        @param showArgs flag indicating to show the arguments
+        @type bool
+        @param environment dictionary of environment settings to add
+            or change for the process
+        @type dict
+        @return flag indicating a successful start of the process
+        @rtype bool
+        """
+        self.errorGroup.hide()
+        self.__normal = False
+        self.__intercept = False
+        
+        if environment is None:
+            environment = {}
+        
+        if showArgs:
+            self.resultbox.append(program + ' ' + ' '.join(args))
+            self.resultbox.append('')
+        
+        self.__process = QProcess()
+        if environment:
+            env = QProcessEnvironment.systemEnvironment()
+            for key, value in environment.items():
+                env.insert(key, value)
+            self.__process.setProcessEnvironment(env)
+        
+        self.__process.finished.connect(self.__procFinished)
+        self.__process.readyReadStandardOutput.connect(self.__readStdout)
+        self.__process.readyReadStandardError.connect(self.__readStderr)
+        
+        if workingDir:
+            self.__process.setWorkingDirectory(workingDir)
+        
+        self.__process.start(program, args)
+        procStarted = self.__process.waitForStarted(10000)
+        if not procStarted:
+            self.buttonBox.setFocus()
+            self.inputGroup.setEnabled(False)
+            E5MessageBox.critical(
+                self,
+                self.tr('Process Generation Error'),
+                self.tr(
+                    '<p>The process <b>{0}</b> could not be started.</p>'
+                ).format(program))
+        else:
+            self.inputGroup.setEnabled(True)
+            self.inputGroup.show()
+        
+        return procStarted
+    
+    def normalExit(self):
+        """
+        Public method to check for a normal process termination.
+        
+        @return flag indicating normal process termination
+        @rtype bool
+        """
+        return self.__normal
+    
+    def normalExitWithoutErrors(self):
+        """
+        Public method to check for a normal process termination without
+        error messages.
+        
+        @return flag indicating normal process termination
+        @rtype bool
+        """
+        return self.__normal and self.errors.toPlainText() == ""
+    
+    def __readStdout(self):
+        """
+        Private slot to handle the readyReadStandardOutput signal.
+        
+        It reads the output of the process and inserts it into the
+        output pane.
+        """
+        if self.__process is not None:
+            s = str(self.__process.readAllStandardOutput(),
+                    Preferences.getSystem("IOEncoding"),
+                    'replace')
+            self.resultbox.insertPlainText(s)
+            self.resultbox.ensureCursorVisible()
+            
+            QCoreApplication.processEvents()
+    
+    def __readStderr(self):
+        """
+        Private slot to handle the readyReadStandardError signal.
+        
+        It reads the error output of the process and inserts it into the
+        error pane.
+        """
+        if self.__process is not None:
+            s = str(self.__process.readAllStandardError(),
+                    Preferences.getSystem("IOEncoding"),
+                    'replace')
+            
+            self.errorGroup.show()
+            self.errors.insertPlainText(s)
+            self.errors.ensureCursorVisible()
+            
+            QCoreApplication.processEvents()
+    
+    def on_passwordCheckBox_toggled(self, isOn):
+        """
+        Private slot to handle the password checkbox toggled.
+        
+        @param isOn flag indicating the status of the check box
+        @type bool
+        """
+        if isOn:
+            self.input.setEchoMode(QLineEdit.Password)
+        else:
+            self.input.setEchoMode(QLineEdit.Normal)
+    
+    @pyqtSlot()
+    def on_sendButton_clicked(self):
+        """
+        Private slot to send the input to the git process.
+        """
+        inputTxt = self.input.text()
+        inputTxt += os.linesep
+        
+        if self.passwordCheckBox.isChecked():
+            self.errors.insertPlainText(os.linesep)
+            self.errors.ensureCursorVisible()
+        else:
+            self.errors.insertPlainText(inputTxt)
+            self.errors.ensureCursorVisible()
+        
+        self.__process.write(strToQByteArray(inputTxt))
+        
+        self.passwordCheckBox.setChecked(False)
+        self.input.clear()
+    
+    def on_input_returnPressed(self):
+        """
+        Private slot to handle the press of the return key in the input field.
+        """
+        self.__intercept = True
+        self.on_sendButton_clicked()
+    
+    def keyPressEvent(self, evt):
+        """
+        Protected slot to handle a key press event.
+        
+        @param evt the key press event (QKeyEvent)
+        """
+        if self.__intercept:
+            self.__intercept = False
+            evt.accept()
+            return
+        
+        super(E5ProcessDialog, self).keyPressEvent(evt)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eric6/E5Gui/E5ProcessDialog.ui	Wed Jul 31 20:41:39 2019 +0200
@@ -0,0 +1,158 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>E5ProcessDialog</class>
+ <widget class="QDialog" name="E5ProcessDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>593</width>
+    <height>499</height>
+   </rect>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout">
+   <item>
+    <widget class="QGroupBox" name="outputGroup">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
+       <horstretch>0</horstretch>
+       <verstretch>2</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="title">
+      <string>Output</string>
+     </property>
+     <layout class="QVBoxLayout">
+      <item>
+       <widget class="QTextEdit" name="resultbox">
+        <property name="readOnly">
+         <bool>true</bool>
+        </property>
+        <property name="acceptRichText">
+         <bool>false</bool>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QLabel" name="statusLabel"/>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="errorGroup">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
+       <horstretch>0</horstretch>
+       <verstretch>1</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="title">
+      <string>Errors</string>
+     </property>
+     <layout class="QVBoxLayout">
+      <item>
+       <widget class="QTextEdit" name="errors">
+        <property name="readOnly">
+         <bool>true</bool>
+        </property>
+        <property name="acceptRichText">
+         <bool>false</bool>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="inputGroup">
+     <property name="title">
+      <string>Input</string>
+     </property>
+     <layout class="QGridLayout">
+      <item row="1" column="1">
+       <spacer>
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeType">
+         <enum>QSizePolicy::Expanding</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>327</width>
+          <height>29</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+      <item row="1" column="2">
+       <widget class="QPushButton" name="sendButton">
+        <property name="toolTip">
+         <string>Press to send the input to the running process</string>
+        </property>
+        <property name="text">
+         <string>&amp;Send</string>
+        </property>
+        <property name="shortcut">
+         <string>Alt+S</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="0" colspan="3">
+       <widget class="E5ClearableLineEdit" name="input">
+        <property name="toolTip">
+         <string>Enter data to be sent to the running process</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="0">
+       <widget class="QCheckBox" name="passwordCheckBox">
+        <property name="toolTip">
+         <string>Select to switch the input field to password mode</string>
+        </property>
+        <property name="text">
+         <string>&amp;Password Mode</string>
+        </property>
+        <property name="shortcut">
+         <string>Alt+P</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Close</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <pixmapfunction>qPixmapFromMimeSource</pixmapfunction>
+ <customwidgets>
+  <customwidget>
+   <class>E5ClearableLineEdit</class>
+   <extends>QLineEdit</extends>
+   <header>E5Gui/E5LineEdit.h</header>
+  </customwidget>
+ </customwidgets>
+ <tabstops>
+  <tabstop>resultbox</tabstop>
+  <tabstop>errors</tabstop>
+  <tabstop>input</tabstop>
+  <tabstop>passwordCheckBox</tabstop>
+  <tabstop>sendButton</tabstop>
+  <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
--- a/eric6/MicroPython/EspDevices.py	Tue Jul 30 19:44:27 2019 +0200
+++ b/eric6/MicroPython/EspDevices.py	Wed Jul 31 20:41:39 2019 +0200
@@ -10,7 +10,14 @@
 
 from __future__ import unicode_literals
 
+import sys
+
 from PyQt5.QtCore import pyqtSlot
+from PyQt5.QtWidgets import QDialog
+
+from E5Gui import E5MessageBox
+from E5Gui.E5ProcessDialog import E5ProcessDialog
+from E5Gui.E5Application import e5App
 
 from .MicroPythonDevices import MicroPythonDevice
 from .MicroPythonReplWidget import HAS_QTCHART
@@ -105,3 +112,111 @@
         Public slot handling a data flood from the device.
         """
         self.microPython.setActionButtons(files=True)
+    
+    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("Erase Flash"),
+                             self.__eraseFlash)
+        act.setEnabled(not connected)
+        act = menu.addAction(self.tr("Flash MicroPython Firmware"),
+                             self.__flashMicroPython)
+        act.setEnabled(not connected)
+        menu.addSeparator()
+        act = menu.addAction(self.tr("Flash Additional Firmware"),
+                             self.__flashAddons)
+        act.setEnabled(not connected)
+        menu.addSeparator()
+        menu.addAction(self.tr("Install 'esptool.py'"), self.__installEspTool)
+    
+    @pyqtSlot()
+    def __eraseFlash(self):
+        """
+        Private slot to erase the device flash memory.
+        """
+        ok = E5MessageBox.yesNo(
+            self,
+            self.tr("Erase Flash"),
+            self.tr("""Shall the flash of the selected device really be"""
+                    """ erased?"""))
+        if ok:
+            flashArgs = [
+                "-u",
+                "-m", "esptool",
+                "--port", self.microPython.getCurrentPort(),
+                "erase_flash",
+            ]
+            dlg = E5ProcessDialog(self.tr("'esptool erase_flash' Output"),
+                                  self.tr("Erase Flash"))
+            res = dlg.startProcess(sys.executable, flashArgs)
+            if res:
+                dlg.exec_()
+    
+    @pyqtSlot()
+    def __flashMicroPython(self):
+        """
+        Private slot to flash a MicroPython firmware to the device.
+        """
+        from .EspFirmwareSelectionDialog import EspFirmwareSelectionDialog
+        dlg = EspFirmwareSelectionDialog()
+        if dlg.exec_() == QDialog.Accepted:
+            chip, firmware, _ = dlg.getData()
+            if chip == "esp8266":
+                flashAddress = "0x0000"
+            elif chip == "esp32":
+                flashAddress = "0x1000"
+            else:
+                raise ValueError(self.tr("Unsupported chip type '{0}'.")
+                                 .format(chip))
+            flashArgs = [
+                "-u",
+                "-m", "esptool",
+                "--chip", chip,
+                "--port", self.microPython.getCurrentPort(),
+                "write_flash",
+                flashAddress,
+                firmware,
+            ]
+            dlg = E5ProcessDialog(self.tr("'esptool write_flash' Output"),
+                                  self.tr("Flash MicroPython Firmware"))
+            res = dlg.startProcess(sys.executable, flashArgs)
+            if res:
+                dlg.exec_()
+    
+    @pyqtSlot()
+    def __flashAddons(self):
+        """
+        Private slot to flash some additional firmware images.
+        """
+        from .EspFirmwareSelectionDialog import EspFirmwareSelectionDialog
+        dlg = EspFirmwareSelectionDialog(addon=True)
+        if dlg.exec_() == QDialog.Accepted:
+            chip, firmware, flashAddress = dlg.getData()
+            flashArgs = [
+                "-u",
+                "-m", "esptool",
+                "--chip", chip,
+                "--port", self.microPython.getCurrentPort(),
+                "write_flash",
+                flashAddress.lower(),
+                firmware,
+            ]
+            dlg = E5ProcessDialog(self.tr("'esptool write_flash' Output"),
+                                  self.tr("Flash Additional Firmware"))
+            res = dlg.startProcess(sys.executable, flashArgs)
+            if res:
+                dlg.exec_()
+    
+    @pyqtSlot()
+    def __installEspTool(self):
+        """
+        Private slot to install the esptool package via pip.
+        """
+        pip = e5App().getObject("Pip")
+        pip.installPackages(["esptool"], interpreter=sys.executable)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eric6/MicroPython/EspFirmwareSelectionDialog.py	Wed Jul 31 20:41:39 2019 +0200
@@ -0,0 +1,109 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2019 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to select the ESP chip type and the firmware to
+be flashed.
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import pyqtSlot, QRegularExpression
+from PyQt5.QtGui import QRegularExpressionValidator
+from PyQt5.QtWidgets import QDialog, QDialogButtonBox
+
+from E5Gui.E5PathPicker import E5PathPickerModes
+
+from .Ui_EspFirmwareSelectionDialog import Ui_EspFirmwareSelectionDialog
+
+
+class EspFirmwareSelectionDialog(QDialog, Ui_EspFirmwareSelectionDialog):
+    """
+    Class implementing a dialog to select the ESP chip type and the firmware to
+    be flashed.
+    """
+    def __init__(self, addon=False, parent=None):
+        """
+        Constructor
+        
+        @param addon flag indicating an addon firmware
+        @type bool
+        @param parent reference to the parent widget
+        @type QWidget
+        """
+        super(EspFirmwareSelectionDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.__addon = addon
+        
+        self.firmwarePicker.setMode(E5PathPickerModes.OpenFileMode)
+        self.firmwarePicker.setFilters(
+            self.tr("Firmware Files (*.bin);;All Files (*)"))
+        
+        self.espComboBox.addItems(["", "ESP32", "ESP8266"])
+        
+        if addon:
+            self.__validator = QRegularExpressionValidator(
+                QRegularExpression(r"[0-9a-fA-F]{0,4}")
+            )
+            self.addressEdit.setValidator(self.__validator)
+        else:
+            self.addressLabel.hide()
+            self.addressEdit.hide()
+        
+        msh = self.minimumSizeHint()
+        self.resize(max(self.width(), msh.width()), msh.height())
+    
+    def __updateOkButton(self):
+        """
+        Private method to update the state of the OK button.
+        """
+        firmwareFile = self.firmwarePicker.text()
+        enable = (bool(self.espComboBox.currentText()) and
+                  bool(firmwareFile) and os.path.exists(firmwareFile))
+        if self.__addon:
+            enable &= bool(self.addressEdit.text())
+        self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(enable)
+    
+    @pyqtSlot(str)
+    def on_espComboBox_currentTextChanged(self, chip):
+        """
+        Private slot to handle the selection of a chip type.
+        
+        @param chip selected chip type 
+        @type str
+        """
+        self.__updateOkButton()
+    
+    @pyqtSlot(str)
+    def on_firmwarePicker_textChanged(self, firmware):
+        """
+        Private slot handling a change of the firmware path.
+        
+        @param firmware path to the firmware
+        @type str
+        """
+        self.__updateOkButton()
+    
+    def getData(self):
+        """
+        Public method to get the entered data.
+        
+        @return tuple containing the selected chip type, the path of the
+            firmware file and the flash address
+        @rtype tuple of (str, str, str)
+        """
+        if self.__addon:
+            address = self.addressEdit.text()
+        else:
+            address = ""
+        
+        return (
+            self.espComboBox.currentText().lower(),
+            self.firmwarePicker.text(),
+            address,
+        )
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eric6/MicroPython/EspFirmwareSelectionDialog.ui	Wed Jul 31 20:41:39 2019 +0200
@@ -0,0 +1,153 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>EspFirmwareSelectionDialog</class>
+ <widget class="QDialog" name="EspFirmwareSelectionDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>500</width>
+    <height>114</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Flash MicroPython Firmware</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QGridLayout" name="gridLayout">
+     <item row="0" column="0">
+      <widget class="QLabel" name="label">
+       <property name="text">
+        <string>ESP Chip Type:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="1">
+      <widget class="QComboBox" name="espComboBox">
+       <property name="toolTip">
+        <string>Select the ESP chip type</string>
+       </property>
+       <property name="sizeAdjustPolicy">
+        <enum>QComboBox::AdjustToContents</enum>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="2">
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item row="1" column="0">
+      <widget class="QLabel" name="label_2">
+       <property name="text">
+        <string>Firmware:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="1" colspan="2">
+      <widget class="E5PathPicker" name="firmwarePicker" native="true">
+       <property name="focusPolicy">
+        <enum>Qt::WheelFocus</enum>
+       </property>
+       <property name="toolTip">
+        <string>Enter the path of the firmware file</string>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="0">
+      <widget class="QLabel" name="addressLabel">
+       <property name="text">
+        <string>Address:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="1" colspan="2">
+      <widget class="E5ClearableLineEdit" name="addressEdit">
+       <property name="toolTip">
+        <string>Enter the flash addres in the hexadecimal form</string>
+       </property>
+       <property name="maxLength">
+        <number>4</number>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>E5PathPicker</class>
+   <extends>QWidget</extends>
+   <header>E5Gui/E5PathPicker.h</header>
+   <container>1</container>
+  </customwidget>
+  <customwidget>
+   <class>E5ClearableLineEdit</class>
+   <extends>QLineEdit</extends>
+   <header>E5Gui/E5LineEdit.h</header>
+  </customwidget>
+ </customwidgets>
+ <tabstops>
+  <tabstop>espComboBox</tabstop>
+  <tabstop>firmwarePicker</tabstop>
+  <tabstop>addressEdit</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>EspFirmwareSelectionDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>EspFirmwareSelectionDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- a/eric6/MicroPython/MicroPythonCommandsInterface.py	Tue Jul 30 19:44:27 2019 +0200
+++ b/eric6/MicroPython/MicroPythonCommandsInterface.py	Wed Jul 31 20:41:39 2019 +0200
@@ -13,7 +13,7 @@
 import time
 import os
 
-from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject, QThread, QTimer
+from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject, QThread, QTimer, QCoreApplication, QEventLoop
 
 from .MicroPythonSerialPort import MicroPythonSerialPort
 
@@ -137,7 +137,7 @@
         if not self.__serial:
             return False
         
-        rawReplMessage = b"raw REPL; CTRL-B to exit\r\n"
+        rawReplMessage = b"raw REPL; CTRL-B to exit\r\n>"
         softRebootMessage = b"soft reboot\r\n"
         
         self.__serial.write(b"\x02")        # end raw mode if required
@@ -160,13 +160,12 @@
         # some MicroPython devices seem to need to be convinced in some
         # special way
         data = self.__serial.readUntil(rawReplMessage)
-        if self.__serial.hasTimedOut():
-            return False
         if not data.endswith(rawReplMessage):
             self.__serial.write(b"\r\x01")  # send CTRL-A again
             self.__serial.readUntil(rawReplMessage)
             if self.__serial.hasTimedOut():
                 return False
+        QCoreApplication.processEvents(QEventLoop.ExcludeUserInputEvents)
         self.__serial.readAll()             # read all data and discard it
         return True
     
@@ -717,7 +716,7 @@
         if err:
             raise IOError(self.__shortError(err))
     
-    def showTime(self):
+    def getTime(self):
         """
         Public method to get the current time of the device.
         
@@ -727,8 +726,17 @@
         """
         commands = [
             "import time",
-            "print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()))",
-            # __IGNORE_WARNING_M601__
+            "\n".join([
+                "try:",
+                "    print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()"
+                "))",
+                # __IGNORE_WARNING_M601__
+                "except AttributeError:",
+                "    tm = time.localtime()",
+                "    print('{0:04d}-{1:02d}-{2:02d} {3:02d}:{4:02d}:{5:02d}'"
+                ".format(tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour,"
+                " tm.tm_min, tm.tm_sec))",
+            ]),
         ]
         out, err = self.execute(commands)
         if err:
--- a/eric6/MicroPython/MicroPythonDevices.py	Tue Jul 30 19:44:27 2019 +0200
+++ b/eric6/MicroPython/MicroPythonDevices.py	Wed Jul 31 20:41:39 2019 +0200
@@ -300,7 +300,7 @@
         """
         pass
     
-    def addActions(self, menu):
+    def addDeviceMenuEntries(self, menu):
         """
         Public method to add device specific entries to the given menu.
         
--- a/eric6/MicroPython/MicroPythonFileManager.py	Tue Jul 30 19:44:27 2019 +0200
+++ b/eric6/MicroPython/MicroPythonFileManager.py	Wed Jul 31 20:41:39 2019 +0200
@@ -45,15 +45,6 @@
     @signal fsinfoDone(fsinfo) emitted after the file system information was
         obtained
     
-    @signal synchTimeDone() emitted after the time was synchronizde to the
-        device
-    @signal showTimeDone(dateTime) emitted after the date and time was fetched
-        from the connected device
-    @signal showVersionDone(versionInfo) emitted after the version information
-        was fetched from the connected device
-    @signal showImplementationDone(name,version) emitted after the
-        implementation information has been obtained
-    
     @signal error(exc) emitted with a failure message to indicate a failure
         during the most recent operation
     """
@@ -69,11 +60,6 @@
     createDirectoryDone = pyqtSignal()
     fsinfoDone = pyqtSignal(tuple)
     
-    synchTimeDone = pyqtSignal()
-    showTimeDone = pyqtSignal(str)
-    showVersionDone = pyqtSignal(dict)
-    showImplementationDone = pyqtSignal(str, str)
-    
     error = pyqtSignal(str, str)
     
     def __init__(self, commandsInterface, parent=None):
@@ -381,65 +367,3 @@
             self.fsinfoDone.emit(fsinfo)
         except Exception as exc:
             self.error.emit("fileSystemInfo", str(exc))
-    
-    ##################################################################
-    ## some non-filesystem related methods below
-    ##################################################################
-    
-    @pyqtSlot()
-    def synchronizeTime(self):
-        """
-        Public slot to set the time of the connected device to the local
-        computer's time.
-        """
-        try:
-            self.__commandsInterface.syncTime()
-            self.synchTimeDone.emit()
-        except Exception as exc:
-            self.error.emit("rmdir", str(exc))
-    
-    @pyqtSlot()
-    def showTime(self):
-        """
-        Public slot to get the current date and time of the device.
-        """
-        try:
-            dt = self.__commandsInterface.showTime()
-            self.showTimeDone.emit(dt)
-        except Exception as exc:
-            self.error.emit("showTime", str(exc))
-    
-    @pyqtSlot()
-    def showVersion(self):
-        """
-        Public slot to get the version info for the MicroPython run by the
-        connected device.
-        """
-        try:
-            versionInfo = self.__commandsInterface.version()
-            self.showVersionDone.emit(versionInfo)
-        except Exception as exc:
-            self.error.emit("showVersion", str(exc))
-    
-    @pyqtSlot()
-    def showImplementation(self):
-        """
-        Public slot to obtain some implementation related information.
-        """
-        try:
-            impInfo = self.__commandsInterface.getImplementation()
-            if impInfo["name"] == "micropython":
-                name = "MicroPython"
-            elif impInfo["name"] == "circuitpython":
-                name = "CircuitPython"
-            elif impInfo["name"] == "unknown":
-                name = self.tr("unknown")
-            else:
-                name = impInfo["name"]
-            if impInfo["version"] == "unknown":
-                version = self.tr("unknown")
-            else:
-                version = impInfo["version"]
-            self.showImplementationDone.emit(name, version)
-        except Exception as exc:
-            self.error.emit("showVersion", str(exc))
--- a/eric6/MicroPython/MicroPythonFileManagerWidget.py	Tue Jul 30 19:44:27 2019 +0200
+++ b/eric6/MicroPython/MicroPythonFileManagerWidget.py	Wed Jul 31 20:41:39 2019 +0200
@@ -85,13 +85,7 @@
         self.__fileManager.removeDirectoryDone.connect(self.__newDeviceList)
         self.__fileManager.createDirectoryDone.connect(self.__newDeviceList)
         self.__fileManager.deleteFileDone.connect(self.__newDeviceList)
-        self.__fileManager.fsinfoDone.connect(self.__shownFsInfoResult)
-        self.__fileManager.synchTimeDone.connect(self.__timeSynchronized)
-        self.__fileManager.showTimeDone.connect(self.__deviceTimeReceived)
-        self.__fileManager.showVersionDone.connect(
-            self.__deviceVersionReceived)
-        self.__fileManager.showImplementationDone.connect(
-            self.__deviceImplementationReceived)
+        self.__fileManager.fsinfoDone.connect(self.__fsInfoResultReceived)
         
         self.__fileManager.error.connect(self.__handleError)
         
@@ -129,16 +123,6 @@
         self.__deviceMenu.addSeparator()
         self.__deviceMenu.addAction(
             self.tr("Show Filesystem Info"), self.__showFileSystemInfo)
-        self.__deviceMenu.addSeparator()
-        self.__deviceMenu.addAction(
-            self.tr("Synchronize Time"), self.__synchronizeTime)
-        self.__deviceMenu.addAction(
-            self.tr("Show Time"), self.__showDeviceTime)
-        self.__deviceMenu.addSeparator()
-        self.__deviceMenu.addAction(
-            self.tr("Show Version"), self.__showDeviceVersion)
-        self.__deviceMenu.addAction(
-            self.tr("Show Implementation"), self.__showImplementation)
     
     def start(self):
         """
@@ -744,9 +728,9 @@
         self.__fileManager.fileSystemInfo()
     
     @pyqtSlot(tuple)
-    def __shownFsInfoResult(self, fsinfo):
+    def __fsInfoResultReceived(self, fsinfo):
         """
-        Private slot to show the file systom information of the device.
+        Private slot to show the file system information of the device.
         
         @param fsinfo tuple of tuples containing the file system name, the
             total size, the used size and the free size
@@ -770,112 +754,3 @@
             self,
             self.tr("Filesystem Information"),
             msg)
-    
-    @pyqtSlot()
-    def __synchronizeTime(self):
-        """
-        Private slot to synchronize the local time to the device.
-        """
-        self.__fileManager.synchronizeTime()
-    
-    @pyqtSlot()
-    def __timeSynchronized(self):
-        """
-        Private slot handling the successful syncronization of the time.
-        """
-        E5MessageBox.information(
-            self,
-            self.tr("Synchronize Time"),
-            self.tr("The time of the connected device was synchronized with"
-                    " the local time."))
-    
-    @pyqtSlot()
-    def __showDeviceTime(self):
-        """
-        Private slot to show the date and time of the connected device.
-        """
-        self.__fileManager.showTime()
-    
-    @pyqtSlot(str)
-    def __deviceTimeReceived(self, dateTimeString):
-        """
-        Private slot handling the receipt of the device date and time.
-        
-        @param dateTimeString string containg the date and time of the device
-        @type str
-        """
-        try:
-            date, time = dateTimeString.strip().split(None, 1)
-            msg = self.tr(
-                "<h3>Device Date and Time</h3>"
-                "<table>"
-                "<tr><td><b>Date</b></td><td>{0}</td></tr>"
-                "<tr><td><b>Time</b></td><td>{1}</td></tr>"
-                "</table>"
-            ).format(date, time)
-        except ValueError:
-            msg = self.tr(
-                "<h3>Device Date and Time</h3>"
-                "<p>{0}</p>"
-            ).format(dateTimeString.strip())
-        E5MessageBox.information(
-            self,
-            self.tr("Device Date and Time"),
-            msg)
-    
-    @pyqtSlot()
-    def __showDeviceVersion(self):
-        """
-        Private slot to show some version info about MicroPython of the device.
-        """
-        self.__fileManager.showVersion()
-    
-    @pyqtSlot(dict)
-    def __deviceVersionReceived(self, versionInfo):
-        """
-        Private slot handling the receipt of the version info.
-        
-        @param versionInfo dictionary containing the version information
-        @type dict
-        """
-        if versionInfo:
-            msg = self.tr(
-                "<h3>Device Version Information</h3>"
-            )
-            msg += "<table>"
-            for key, value in versionInfo.items():
-                msg += "<tr><td><b>{0}</b></td><td>{1}</td></tr>".format(
-                    key.capitalize(), value)
-            msg += "</table>"
-        else:
-            msg = self.tr("No version information available.")
-        E5MessageBox.information(
-            self,
-            self.tr("Device Version Information"),
-            msg)
-    
-    @pyqtSlot()
-    def __showImplementation(self):
-        """
-        Private slot to show some implementation related information.
-        """
-        self.__fileManager.showImplementation()
-    
-    @pyqtSlot(str, str)
-    def __deviceImplementationReceived(self, name, version):
-        """
-        Private slot handling the receipt of implementation info.
-        
-        @param name name of the implementation
-        @type str
-        @param version version string of the implementation
-        @type str
-        """
-        E5MessageBox.information(
-            self,
-            self.tr("Device Implementation Information"),
-            self.tr(
-                "<h3>Device Implementation Information</h3>"
-                "<p>This device contains <b>{0} {1}</b>.</p>"
-            ).format(name, version)
-        )
--- a/eric6/MicroPython/MicroPythonReplWidget.py	Tue Jul 30 19:44:27 2019 +0200
+++ b/eric6/MicroPython/MicroPythonReplWidget.py	Wed Jul 31 20:41:39 2019 +0200
@@ -15,7 +15,7 @@
 from PyQt5.QtGui import QColor, QKeySequence, QTextCursor, QBrush
 from PyQt5.QtWidgets import (
     QWidget, QMenu, QApplication, QHBoxLayout, QSpacerItem, QSizePolicy,
-    QTextEdit
+    QTextEdit, QToolButton
 )
 
 from E5Gui.E5ZoomWidget import E5ZoomWidget
@@ -163,6 +163,20 @@
         
         self.__ui = parent
         
+        self.__superMenu = QMenu(self)
+        self.__superMenu.aboutToShow.connect(self.__aboutToShowSuperMenu)
+        
+        self.menuButton.setObjectName(
+            "micropython_supermenu_button")
+        self.menuButton.setIcon(UI.PixmapCache.getIcon("superMenu"))
+        self.menuButton.setToolTip(self.tr("pip Menu"))
+        self.menuButton.setPopupMode(QToolButton.InstantPopup)
+        self.menuButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
+        self.menuButton.setFocusPolicy(Qt.NoFocus)
+        self.menuButton.setAutoRaise(True)
+        self.menuButton.setShowMenuInside(True)
+        self.menuButton.setMenu(self.__superMenu)
+        
         self.deviceIconLabel.setPixmap(MicroPythonDevices.getDeviceIcon(
             "", False))
         
@@ -345,9 +359,6 @@
         menu.addAction(self.tr("Copy"), self.replEdit.copy, copyKeys)
         menu.addAction(self.tr("Paste"), self.__paste, pasteKeys)
         menu.addSeparator()
-        if self.__device is not None:
-            # allow device interface to add specific context menu entries
-            self.__device.addActions(menu)
         menu.exec_(self.replEdit.mapToGlobal(pos))
     
     def setConnected(self, connected):
@@ -376,6 +387,15 @@
             self.connectButton.setToolTip(self.tr(
                 "Press to connect the selected device"))
     
+    def isConnected(self):
+        """
+        Public method to get the connection state.
+        
+        @return connection state
+        @rtype bool
+        """
+        return self.__connected
+    
     def __showNoDeviceMessage(self):
         """
         Private method to show a message dialog indicating a missing device.
@@ -748,9 +768,9 @@
             self.replEdit.zoomIn(value - self.__currentZoom)
         self.__currentZoom = value
     
-    def __getCurrentPort(self):
+    def getCurrentPort(self):
         """
-        Private method to determine the port path of the selected device.
+        Public method to determine the port path of the selected device.
         
         @return path of the port of the selected device
         @rtype str
@@ -770,7 +790,7 @@
         """
         Private method to connect to the selected device.
         """
-        port = self.__getCurrentPort()
+        port = self.getCurrentPort()
         if self.__interface.connectToDevice(port):
             self.setConnected(True)
         else:
@@ -999,3 +1019,149 @@
             
             self.__fileManagerWidget.deleteLater()
             self.__fileManagerWidget = None
+    
+    ##################################################################
+    ## Super Menu related methods below
+    ##################################################################
+    
+    def __aboutToShowSuperMenu(self):
+        """
+        Private slot to populate the Super Menu before showing it.
+        """
+        self.__superMenu.clear()
+        
+        act = self.__superMenu.addAction(
+            self.tr("Show Version"), self.__showDeviceVersion)
+        act.setEnabled(self.__connected)
+        act = self.__superMenu.addAction(
+            self.tr("Show Implementation"), self.__showImplementation)
+        act.setEnabled(self.__connected)
+        self.__superMenu.addSeparator()
+        act = self.__superMenu.addAction(
+            self.tr("Synchronize Time"), self.__synchronizeTime)
+        act.setEnabled(self.__connected)
+        act = self.__superMenu.addAction(
+            self.tr("Show Time"), self.__showDeviceTime)
+        act.setEnabled(self.__connected)
+        self.__superMenu.addSeparator()
+        if self.__device:
+            self.__device.addDeviceMenuEntries(self.__superMenu)
+    
+    @pyqtSlot()
+    def __showDeviceVersion(self):
+        """
+        Private slot to show some version info about MicroPython of the device.
+        """
+        try:
+            versionInfo = self.__interface.version()
+            if versionInfo:
+                msg = self.tr(
+                    "<h3>Device Version Information</h3>"
+                )
+                msg += "<table>"
+                for key, value in versionInfo.items():
+                    msg += "<tr><td><b>{0}</b></td><td>{1}</td></tr>".format(
+                        key.capitalize(), value)
+                msg += "</table>"
+            else:
+                msg = self.tr("No version information available.")
+            
+            E5MessageBox.information(
+                self,
+                self.tr("Device Version Information"),
+                msg)
+        except Exception as exc:
+            self.__showError("version()", str(exc))
+    
+    @pyqtSlot()
+    def __showImplementation(self):
+        """
+        Private slot to show some implementation related information.
+        """
+        try:
+            impInfo = self.__interface.getImplementation()
+            if impInfo["name"] == "micropython":
+                name = "MicroPython"
+            elif impInfo["name"] == "circuitpython":
+                name = "CircuitPython"
+            elif impInfo["name"] == "unknown":
+                name = self.tr("unknown")
+            else:
+                name = impInfo["name"]
+            if impInfo["version"] == "unknown":
+                version = self.tr("unknown")
+            else:
+                version = impInfo["version"]
+            
+            E5MessageBox.information(
+                self,
+                self.tr("Device Implementation Information"),
+                self.tr(
+                    "<h3>Device Implementation Information</h3>"
+                    "<p>This device contains <b>{0} {1}</b>.</p>"
+                ).format(name, version)
+            )
+        except Exception as exc:
+            self.__showError("getImplementation()", str(exc))
+    
+    @pyqtSlot()
+    def __synchronizeTime(self):
+        """
+        Private slot to set the time of the connected device to the local
+        computer's time.
+        """
+        try:
+            self.__interface.syncTime()
+            
+            E5MessageBox.information(
+                self,
+                self.tr("Synchronize Time"),
+                self.tr("The time of the connected device was synchronized"
+                        " with the local time."))
+        except Exception as exc:
+            self.__showError("syncTime()", str(exc))
+    
+    @pyqtSlot()
+    def __showDeviceTime(self):
+        """
+        Private slot to show the date and time of the connected device.
+        """
+        try:
+            dateTimeString = self.__interface.getTime()
+            try:
+                date, time = dateTimeString.strip().split(None, 1)
+                msg = self.tr(
+                    "<h3>Device Date and Time</h3>"
+                    "<table>"
+                    "<tr><td><b>Date</b></td><td>{0}</td></tr>"
+                    "<tr><td><b>Time</b></td><td>{1}</td></tr>"
+                    "</table>"
+                ).format(date, time)
+            except ValueError:
+                msg = self.tr(
+                    "<h3>Device Date and Time</h3>"
+                    "<p>{0}</p>"
+                ).format(dateTimeString.strip())
+            
+            E5MessageBox.information(
+                self,
+                self.tr("Device Date and Time"),
+                msg)
+        except Exception as exc:
+            self.__showError("getTime()", str(exc))
+    
+    def __showError(self, method, error):
+        """
+        Private method to show some error message.
+        
+        @param method name of the method the error occured in
+        @type str
+        @param error error message
+        @type str
+        """
+        E5MessageBox.warning(
+            self,
+            self.tr("Error handling device"),
+            self.tr("<p>There was an error communicating with the connected"
+                    " device.</p><p>Method: {0}</p><p>Message: {1}</p>")
+            .format(method, error))
--- a/eric6/MicroPython/MicroPythonReplWidget.ui	Tue Jul 30 19:44:27 2019 +0200
+++ b/eric6/MicroPython/MicroPythonReplWidget.ui	Wed Jul 31 20:41:39 2019 +0200
@@ -31,7 +31,7 @@
      </item>
      <item>
       <layout class="QGridLayout" name="gridLayout">
-       <item row="1" column="0" colspan="3">
+       <item row="1" column="0" colspan="4">
         <widget class="QLabel" name="deviceInfoLabel">
          <property name="sizePolicy">
           <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
@@ -44,6 +44,9 @@
          </property>
         </widget>
        </item>
+       <item row="0" column="3">
+        <widget class="E5Led" name="deviceConnectedLed" native="true"/>
+       </item>
        <item row="0" column="0">
         <widget class="QComboBox" name="deviceTypeComboBox">
          <property name="sizePolicy">
@@ -54,9 +57,6 @@
          </property>
         </widget>
        </item>
-       <item row="0" column="2">
-        <widget class="E5Led" name="deviceConnectedLed" native="true"/>
-       </item>
        <item row="0" column="1">
         <widget class="QToolButton" name="checkButton">
          <property name="toolTip">
@@ -64,6 +64,9 @@
          </property>
         </widget>
        </item>
+       <item row="0" column="2">
+        <widget class="E5ToolButton" name="menuButton"/>
+       </item>
       </layout>
      </item>
     </layout>
@@ -169,13 +172,15 @@
      </property>
     </widget>
    </item>
-   <item>
-    <widget class="QLabel" name="statusLabel"/>
-   </item>
   </layout>
  </widget>
  <customwidgets>
   <customwidget>
+   <class>E5ToolButton</class>
+   <extends>QToolButton</extends>
+   <header>E5Gui/E5ToolButton.h</header>
+  </customwidget>
+  <customwidget>
    <class>E5Led</class>
    <extends>QWidget</extends>
    <header>E5Gui/E5Led.h</header>
@@ -185,6 +190,7 @@
  <tabstops>
   <tabstop>deviceTypeComboBox</tabstop>
   <tabstop>checkButton</tabstop>
+  <tabstop>menuButton</tabstop>
   <tabstop>openButton</tabstop>
   <tabstop>saveButton</tabstop>
   <tabstop>runButton</tabstop>
--- a/eric6/Preferences/__init__.py	Tue Jul 30 19:44:27 2019 +0200
+++ b/eric6/Preferences/__init__.py	Wed Jul 31 20:41:39 2019 +0200
@@ -1592,7 +1592,7 @@
     
     # defaults for MicroPython
     microPythonDefaults = {
-        "SerialTimeout": 10000,         # timeout in milliseconds
+        "SerialTimeout": 2000,         # timeout in milliseconds
         "ReplLineWrap": True,           # wrap the REPL lines
     }
     if Globals.isWindowsPlatform():

eric ide

mercurial