Mon, 22 Jul 2019 20:17:33 +0200
MicroPython: continued implementing the file manager widget.
# -*- 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 os import ctypes from subprocess import check_output from E5Gui import E5MessageBox from .MicroPythonDevices import MicroPythonDevice from .MicroPythonReplWidget import HAS_QTCHART import Globals class CircuitPythonDevice(MicroPythonDevice): """ Class implementing the device for CircuitPython boards. """ def __init__(self, microPythonWidget, parent=None): """ Constructor @param microPythonWidget reference to the main MicroPython widget @type MicroPythonReplWidget @param parent reference to the parent object @type QObject """ super(CircuitPythonDevice, self).__init__(microPythonWidget, parent) self.__replActive = False self.__plotterActive = False def setButtons(self): """ Public method to enable the supported action buttons. """ super(CircuitPythonDevice, self).setButtons() self.microPython.setActionButtons(repl=True, chart=HAS_QTCHART) 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 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 setRepl(self, on): """ Public method to set the REPL status and dependent status. @param on flag indicating the active status @type bool """ self.__replActive = on 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 setPlotter(self, on): """ Public method to set the Plotter status and dependent status. @param on flag indicating the active status @type bool """ self.__plotterActive = on def getWorkspace(self): """ Public method to get the workspace directory. @return workspace directory used for saving files @rtype str """ deviceDirectory = None # Attempts to find the path on the filesystem that represents the # plugged in CIRCUITPY board. if Globals.isWindowsPlatform(): # we are on a Windows platform def getVolumeName(diskName): """ Local function to determine the volume of a disk or device. Each disk or external device connected to windows has an attribute called "volume name". This function returns the volume name for the given disk/device. Code from http://stackoverflow.com/a/12056414 """ volumeNameBuffer = ctypes.create_unicode_buffer(1024) ctypes.windll.kernel32.GetVolumeInformationW( ctypes.c_wchar_p(diskName), volumeNameBuffer, ctypes.sizeof(volumeNameBuffer), None, None, None, None, 0) return volumeNameBuffer.value # # In certain circumstances, volumes are allocated to USB # storage devices which cause a Windows popup to raise if their # volume contains no media. Wrapping the check in SetErrorMode # with SEM_FAILCRITICALERRORS (1) prevents this popup. # oldMode = ctypes.windll.kernel32.SetErrorMode(1) try: for disk in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ': path = '{0}:\\'.format(disk) if (os.path.exists(path) and getVolumeName(path) == 'CIRCUITPY'): return path finally: ctypes.windll.kernel32.SetErrorMode(oldMode) else: # we are on a Linux or macOS platform for mountCommand in ['mount', '/sbin/mount']: try: mountOutput = check_output(mountCommand).splitlines() mountedVolumes = [x.split()[2] for x in mountOutput] for volume in mountedVolumes: if volume.endswith(b'CIRCUITPY'): deviceDirectory = volume.decode('utf-8') except FileNotFoundError: next if deviceDirectory: return deviceDirectory else: # return the default workspace and give the user a warning E5MessageBox.warning( self.microPythonWidget, 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()