eric6/MicroPython/CircuitPythonDevices.py

Thu, 15 Aug 2019 17:04:54 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Thu, 15 Aug 2019 17:04:54 +0200
branch
micropython
changeset 7138
a2a53535d855
parent 7134
21d23ca51680
child 7145
ceb3e8b242c1
permissions
-rw-r--r--

CircuitPythonDevices: added capability to install library files onto the device to the device menu.

# -*- coding: utf-8 -*-

# Copyright (c) 2019 Detlev Offenbach <detlev@die-offenbachs.de>
#

"""
Module implementing the device interface class for CircuitPython boards.
"""

from __future__ import unicode_literals

import shutil
import os

from PyQt5.QtCore import pyqtSlot
from PyQt5.QtWidgets import QDialog

from E5Gui import E5MessageBox, E5FileDialog

from .MicroPythonDevices import MicroPythonDevice
from .MicroPythonWidget import HAS_QTCHART

import Utilities


class CircuitPythonDevice(MicroPythonDevice):
    """
    Class implementing the device for CircuitPython boards.
    """
    DeviceVolumeName = "CIRCUITPY"
    
    def __init__(self, microPythonWidget, parent=None):
        """
        Constructor
        
        @param microPythonWidget reference to the main MicroPython widget
        @type MicroPythonWidget
        @param parent reference to the parent object
        @type QObject
        """
        super(CircuitPythonDevice, self).__init__(microPythonWidget, parent)
    
    def setButtons(self):
        """
        Public method to enable the supported action buttons.
        """
        super(CircuitPythonDevice, self).setButtons()
        self.microPython.setActionButtons(
            run=True, repl=True, files=True, chart=HAS_QTCHART)
        
        if self.__deviceVolumeMounted():
            self.microPython.setActionButtons(open=True, save=True)
    
    def forceInterrupt(self):
        """
        Public method to determine the need for an interrupt when opening the
        serial connection.
        
        @return flag indicating an interrupt is needed
        @rtype bool
        """
        return False
    
    def deviceName(self):
        """
        Public method to get the name of the device.
        
        @return name of the device
        @rtype str
        """
        return self.tr("CircuitPython")
    
    def canStartRepl(self):
        """
        Public method to determine, if a REPL can be started.
        
        @return tuple containing a flag indicating it is safe to start a REPL
            and a reason why it cannot.
        @rtype tuple of (bool, str)
        """
        return True, ""
    
    def canStartPlotter(self):
        """
        Public method to determine, if a Plotter can be started.
        
        @return tuple containing a flag indicating it is safe to start a
            Plotter and a reason why it cannot.
        @rtype tuple of (bool, str)
        """
        return True, ""
    
    def canRunScript(self):
        """
        Public method to determine, if a script can be executed.
        
        @return tuple containing a flag indicating it is safe to start a
            Plotter and a reason why it cannot.
        @rtype tuple of (bool, str)
        """
        return True, ""
    
    def runScript(self, script):
        """
        Public method to run the given Python script.
        
        @param script script to be executed
        @type str
        """
        pythonScript = script.split("\n")
        self.sendCommands(pythonScript)
    
    def canStartFileManager(self):
        """
        Public method to determine, if a File Manager can be started.
        
        @return tuple containing a flag indicating it is safe to start a
            File Manager and a reason why it cannot.
        @rtype tuple of (bool, str)
        """
        return True, ""
    
    def supportsLocalFileAccess(self):
        """
        Public method to indicate file access via a local directory.
        
        @return flag indicating file access via local directory
        @type bool
        """
        return self.__deviceVolumeMounted()
    
    def __deviceVolumeMounted(self):
        """
        Private method to check, if the device volume is mounted.
        
        @return flag indicated a mounted device
        @rtype bool
        """
        return self.getWorkspace().endswith(self.DeviceVolumeName)
    
    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 CIRCUITPY board.
        deviceDirectory = Utilities.findVolume(self.DeviceVolumeName)
        
        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("Python files for CircuitPython devices are stored on"
                        " the device. Therefore, to edit these files you need"
                        " to have the device plugged in. Until you plug in a"
                        " device, the standard directory will be used."))
            
            return super(CircuitPythonDevice, self).getWorkspace()
    
    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 CircuitPython Firmware"),
                             self.__flashCircuitPython)
        act.setEnabled(not connected)
        menu.addSeparator()
        act = menu.addAction(self.tr("Install Library Files"),
                             self.__installLibraryFiles)
        act.setEnabled(self.__deviceVolumeMounted())
    
    @pyqtSlot()
    def __flashCircuitPython(self):
        """
        Private slot to flash a CircuitPython firmware to the device.
        """
        ok = E5MessageBox.information(
            self.microPython,
            self.tr("Flash CircuitPython Firmware"),
            self.tr("Please reset the device to bootloader mode and confirm"
                    " when ready."),
            E5MessageBox.StandardButtons(
                E5MessageBox.Abort |
                E5MessageBox.Ok))
        if ok:
            from .CircuitPythonFirmwareSelectionDialog import (
                CircuitPythonFirmwareSelectionDialog)
            dlg = CircuitPythonFirmwareSelectionDialog()
            if dlg.exec_() == QDialog.Accepted:
                cpyPath, devicePath = dlg.getData()
                shutil.copy2(cpyPath, devicePath)
    
    @pyqtSlot()
    def __installLibraryFiles(self):
        """
        Private slot to install Python files into the onboard library.
        """
        if not self.__deviceVolumeMounted():
            E5MessageBox.critical(
                self.microPython,
                self.tr("Install Library Files"),
                self.tr("""The device volume "<b>{0}</b>" is not available."""
                        """ Ensure it is mounted properly and try again."""))
            return
        
        target = os.path.join(self.getWorkspace(), "lib")
        # ensure that the library directory exists on the device
        if not os.path.isdir(target):
            os.makedirs(target)
        
        libraryFiles = E5FileDialog.getOpenFileNames(
            self.microPython,
            self.tr("Install Library Files"),
            os.path.expanduser("~"),
            self.tr("Compiled Python Files (*.mpy);;"
                    "Python Files (*.py);;"
                    "All Files (*)"))
        
        for libraryFile in libraryFiles:
            if os.path.exists(libraryFile):
                shutil.copy2(libraryFile, target)

eric ide

mercurial