Esempio n. 1
0
    def __init__(self, parent=None, mode="python"):
        super().__init__(parent)
        self.mode = mode
        self.msg_duration = 5

        # Mode selector.
        self.mode_label = QLabel()
        self.mode_label.setToolTip(("Mu's current mode of behaviour."))
        self.addPermanentWidget(self.mode_label)
        self.set_mode(mode)

        # Device selector.
        self.device_selector = DeviceSelector()
        self.device_selector.setHidden(True)
        self.addPermanentWidget(self.device_selector)

        # Logs viewer
        self.logs_label = QLabel()
        self.logs_label.setObjectName("AdministrationLabel")
        self.logs_label.setPixmap(load_pixmap("logs").scaledToHeight(24))
        self.logs_label.setToolTip(("Mu Administration"))
        self.addPermanentWidget(self.logs_label)
Esempio n. 2
0
    def setup(self, mode, device_list):
        widget_layout = QVBoxLayout()
        self.setLayout(widget_layout)

        # Instructions
        grp_instructions = QGroupBox(
            _("How to flash MicroPython to your device")
        )
        grp_instructions_vbox = QVBoxLayout()
        grp_instructions.setLayout(grp_instructions_vbox)
        # Note: we have to specify the link color here, to something
        # that's suitable for both day/night/contrast themes, as the
        # link color is not configurable in the Qt Stylesheets
        instructions = _(
            "&nbsp;1. Determine the type of device (ESP8266 or ESP32)<br />"
            "&nbsp;2. Download firmware from the "
            '<a href="https://micropython.org/download" '
            'style="color:#039be5;">'
            "https://micropython.org/download</a><br/>"
            "&nbsp;3. Connect your device<br/>"
            "&nbsp;4. Load the .bin file below using the 'Browse' button<br/>"
            "&nbsp;5. Press 'Erase & write firmware'"
            # "<br /><br />Check the current MicroPython version using the "
            # "following commands:<br />"
            # ">>> import sys<br />"
            # ">>> sys.implementation"
        )
        label = QLabel(instructions)
        label.setTextFormat(Qt.RichText)
        label.setTextInteractionFlags(Qt.TextBrowserInteraction)
        label.setOpenExternalLinks(True)
        label.setWordWrap(True)
        grp_instructions_vbox.addWidget(label)
        widget_layout.addWidget(grp_instructions)

        # Device type, firmware path, flash button
        device_selector_label = QLabel("Device:")
        self.device_selector = DeviceSelector(show_label=True, icon_first=True)
        self.device_selector.set_device_list(device_list)
        device_type_label = QLabel("Choose device type:")
        self.device_type = QComboBox(self)
        self.device_type.addItem("ESP8266")
        self.device_type.addItem("ESP32")
        firmware_label = QLabel("Firmware (.bin):")
        self.txtFolder = QLineEdit()
        self.btnFolder = QPushButton(_("Browse"))
        self.btnExec = QPushButton(_("Erase && write firmware"))
        self.btnExec.setEnabled(False)
        form_set = QGridLayout()
        form_set.addWidget(device_selector_label, 0, 0)
        form_set.addWidget(self.device_selector, 0, 1, 1, 3)
        form_set.addWidget(device_type_label, 1, 0)
        form_set.addWidget(self.device_type, 1, 1)
        form_set.addWidget(firmware_label, 2, 0)
        form_set.addWidget(self.txtFolder, 2, 1)
        form_set.addWidget(self.btnFolder, 2, 2)
        form_set.addWidget(self.btnExec, 2, 3)
        widget_layout.addLayout(form_set)

        # Output area
        self.log_text_area = QPlainTextEdit()
        self.log_text_area.setReadOnly(True)
        form_set = QHBoxLayout()
        form_set.addWidget(self.log_text_area)
        widget_layout.addLayout(form_set)

        # Connect events
        self.txtFolder.textChanged.connect(self.firmware_path_changed)
        self.btnFolder.clicked.connect(self.show_folder_dialog)
        self.btnExec.clicked.connect(self.update_firmware)
        self.device_selector.device_changed.connect(self.toggle_exec_button)

        self.mode = mode
Esempio n. 3
0
class ESPFirmwareFlasherWidget(QWidget):
    """
    Used for configuring how to interact with the ESP:

    * Override MicroPython.
    """

    def setup(self, mode, device_list):
        widget_layout = QVBoxLayout()
        self.setLayout(widget_layout)

        # Instructions
        grp_instructions = QGroupBox(
            _("How to flash MicroPython to your device")
        )
        grp_instructions_vbox = QVBoxLayout()
        grp_instructions.setLayout(grp_instructions_vbox)
        # Note: we have to specify the link color here, to something
        # that's suitable for both day/night/contrast themes, as the
        # link color is not configurable in the Qt Stylesheets
        instructions = _(
            "&nbsp;1. Determine the type of device (ESP8266 or ESP32)<br />"
            "&nbsp;2. Download firmware from the "
            '<a href="https://micropython.org/download" '
            'style="color:#039be5;">'
            "https://micropython.org/download</a><br/>"
            "&nbsp;3. Connect your device<br/>"
            "&nbsp;4. Load the .bin file below using the 'Browse' button<br/>"
            "&nbsp;5. Press 'Erase & write firmware'"
            # "<br /><br />Check the current MicroPython version using the "
            # "following commands:<br />"
            # ">>> import sys<br />"
            # ">>> sys.implementation"
        )
        label = QLabel(instructions)
        label.setTextFormat(Qt.RichText)
        label.setTextInteractionFlags(Qt.TextBrowserInteraction)
        label.setOpenExternalLinks(True)
        label.setWordWrap(True)
        grp_instructions_vbox.addWidget(label)
        widget_layout.addWidget(grp_instructions)

        # Device type, firmware path, flash button
        device_selector_label = QLabel("Device:")
        self.device_selector = DeviceSelector(show_label=True, icon_first=True)
        self.device_selector.set_device_list(device_list)
        device_type_label = QLabel("Choose device type:")
        self.device_type = QComboBox(self)
        self.device_type.addItem("ESP8266")
        self.device_type.addItem("ESP32")
        firmware_label = QLabel("Firmware (.bin):")
        self.txtFolder = QLineEdit()
        self.btnFolder = QPushButton(_("Browse"))
        self.btnExec = QPushButton(_("Erase && write firmware"))
        self.btnExec.setEnabled(False)
        form_set = QGridLayout()
        form_set.addWidget(device_selector_label, 0, 0)
        form_set.addWidget(self.device_selector, 0, 1, 1, 3)
        form_set.addWidget(device_type_label, 1, 0)
        form_set.addWidget(self.device_type, 1, 1)
        form_set.addWidget(firmware_label, 2, 0)
        form_set.addWidget(self.txtFolder, 2, 1)
        form_set.addWidget(self.btnFolder, 2, 2)
        form_set.addWidget(self.btnExec, 2, 3)
        widget_layout.addLayout(form_set)

        # Output area
        self.log_text_area = QPlainTextEdit()
        self.log_text_area.setReadOnly(True)
        form_set = QHBoxLayout()
        form_set.addWidget(self.log_text_area)
        widget_layout.addLayout(form_set)

        # Connect events
        self.txtFolder.textChanged.connect(self.firmware_path_changed)
        self.btnFolder.clicked.connect(self.show_folder_dialog)
        self.btnExec.clicked.connect(self.update_firmware)
        self.device_selector.device_changed.connect(self.toggle_exec_button)

        self.mode = mode

    def esptool_is_installed(self):
        """
        Is the 'esptool' package installed?
        """
        baseline, user_packages = venv.installed_packages()
        return "esptool" in user_packages

    def show_folder_dialog(self):
        # open dialog and set to foldername
        filename = QFileDialog.getOpenFileName(
            self,
            "Select MicroPython firmware (.bin)",
            os.path.expanduser("."),
            "Firmware (*.bin)",
        )
        if filename:
            filename = filename[0].replace("/", os.sep)
            self.txtFolder.setText(filename)

    def update_firmware(self):
        baudrate = 115200

        if self.mode.repl:
            self.mode.toggle_repl(None)
        if self.mode.plotter:
            self.mode.toggle_plotter(None)
        if self.mode.fs is not None:
            self.mode.toggle_files(None)

        device = self.device_selector.selected_device()
        if device is None:
            return

        esptool = "-mesptool"
        erase_command = '"{}" "{}" --port {} erase_flash'.format(
            venv.interpreter, esptool, device.port
        )

        if self.device_type.currentText() == "ESP32":
            write_command = (
                '"{}" "{}" --chip esp32 --port {} --baud {} '
                'write_flash -z 0x1000 "{}"'
            ).format(
                venv.interpreter,
                esptool,
                device.port,
                baudrate,
                self.txtFolder.text(),
            )
        else:
            write_command = (
                '"{}" "{}" --chip esp8266 --port {} --baud {} '
                'write_flash --flash_size=detect 0 "{}"'
            ).format(
                venv.interpreter,
                esptool,
                device.port,
                baudrate,
                self.txtFolder.text(),
            )

        self.commands = [erase_command, write_command]
        self.run_esptool()

    def run_esptool(self):
        self.process = QProcess(self)
        self.process.setProcessChannelMode(QProcess.MergedChannels)
        self.process.readyReadStandardError.connect(self.read_process)
        self.process.readyReadStandardOutput.connect(self.read_process)
        self.process.finished.connect(self.esptool_finished)
        self.process.error.connect(self.esptool_error)

        command = self.commands.pop(0)
        self.log_text_area.appendPlainText(command + "\n")
        self.process.start(command)

    def esptool_error(self, error_num):
        self.log_text_area.appendPlainText(
            "Error occurred: Error {}\n".format(error_num)
        )
        self.process = None

    def esptool_finished(self, exitCode, exitStatus):
        """
        Called when the subprocess that executes 'esptool.py is finished.
        """
        # Exit if a command fails
        if exitCode != 0 or exitStatus == QProcess.CrashExit:
            self.log_text_area.appendPlainText("Error on flashing. Aborting.")
            return
        if self.commands:
            self.process = None
            self.run_esptool()

    def read_process(self):
        """
        Read data from the child process and append it to the text area. Try
        to keep reading until there's no more data from the process.
        """
        msg = ""
        data = self.process.readAll()
        if data:
            try:
                msg = data.data().decode("utf-8")
                self.append_data(msg)
            except UnicodeDecodeError:
                pass
            QTimer.singleShot(2, self.read_process)

    def append_data(self, msg):
        """
        Add data to the end of the text area.
        """
        cursor = self.log_text_area.textCursor()
        cursor.movePosition(QTextCursor.End)
        cursor.insertText(msg)
        cursor.movePosition(QTextCursor.End)
        self.log_text_area.setTextCursor(cursor)

    def firmware_path_changed(self):
        self.toggle_exec_button()

    def toggle_exec_button(self):
        if (
            len(self.txtFolder.text()) > 0
            and self.device_selector.selected_device() is not None
        ):
            self.btnExec.setEnabled(True)
        else:
            self.btnExec.setEnabled(False)
Esempio n. 4
0
class StatusBar(QStatusBar):
    """
    Defines the look and behaviour of the status bar along the bottom of the
    UI.
    """
    def __init__(self, parent=None, mode="python"):
        super().__init__(parent)
        self.mode = mode
        self.msg_duration = 5

        # Mode selector.
        self.mode_label = QLabel()
        self.mode_label.setToolTip(("Mu's current mode of behaviour."))
        self.addPermanentWidget(self.mode_label)
        self.set_mode(mode)

        # Device selector.
        self.device_selector = DeviceSelector()
        self.device_selector.setHidden(True)
        self.addPermanentWidget(self.device_selector)

        # Logs viewer
        self.logs_label = QLabel()
        self.logs_label.setObjectName("AdministrationLabel")
        self.logs_label.setPixmap(load_pixmap("logs").scaledToHeight(24))
        self.logs_label.setToolTip(("Mu Administration"))
        self.addPermanentWidget(self.logs_label)

    def connect_logs(self, handler, shortcut):
        """
        Connect the mouse press event and keyboard shortcut for the log widget
        to the referenced handler function.
        """
        self.logs_label.shortcut = QShortcut(QKeySequence(shortcut),
                                             self.parent())
        self.logs_label.shortcut.activated.connect(handler)
        self.logs_label.mousePressEvent = handler

    def connect_mode(self, handler, shortcut):
        """
        Connect the mouse press event and keyboard shortcut for the mode widget
        to the referenced handler function.
        """
        self.mode_label.shortcut = QShortcut(QKeySequence(shortcut),
                                             self.parent())
        self.mode_label.shortcut.activated.connect(handler)
        self.mode_label.mousePressEvent = handler

    def set_message(self, message, pause=5000):
        """
        Displays a message in the status bar for a certain period of time.
        """
        self.showMessage(message, pause)

    def set_mode(self, mode):
        """
        Updates the mode label to the new mode.
        """
        self.mode_label.setText(mode)

    def device_connected(self, device):
        """
        Show a tooltip whenever a new device connects
        """
        msg = ("Detected new device: {}.").format(device.port)
        self.set_message(msg, self.msg_duration * 1000)

    def device_disconnected(self, device):
        msg = ("Off-line device: {}.").format(device.port)
        self.set_message(msg, self.msg_duration * 1000)