eric6/MicroPython/CircuitPythonDevices.py

branch
micropython
changeset 7059
a8fad276cbd5
child 7065
e3d04faced34
equal deleted inserted replaced
7058:bdd583f96e96 7059:a8fad276cbd5
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2019 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the device interface class for CircuitPython boards.
8 """
9
10 from __future__ import unicode_literals
11
12 import os
13 import ctypes
14 from subprocess import check_output
15
16 from E5Gui import E5MessageBox
17
18 from .MicroPythonDevices import MicroPythonDevice
19 from .MicroPythonReplWidget import HAS_QTCHART
20
21 import Globals
22
23
24 class CircuitPythonDevice(MicroPythonDevice):
25 """
26 Class implementing the device for CircuitPython boards.
27 """
28 def __init__(self, microPythonWidget, parent=None):
29 """
30 Constructor
31
32 @param microPythonWidget reference to the main MicroPython widget
33 @type MicroPythonReplWidget
34 @param parent reference to the parent object
35 @type QObject
36 """
37 super(CircuitPythonDevice, self).__init__(microPythonWidget, parent)
38
39 self.__replActive = False
40 self.__plotterActive = False
41
42 def setButtons(self):
43 """
44 Public method to enable the supported action buttons.
45 """
46 super(CircuitPythonDevice, self).setButtons()
47 self.microPython.setActionButtons(repl=True, chart=HAS_QTCHART)
48
49 def forceInterrupt(self):
50 """
51 Public method to determine the need for an interrupt when opening the
52 serial connection.
53
54 @return flag indicating an interrupt is needed
55 @rtype bool
56 """
57 return False
58
59 def canStartRepl(self):
60 """
61 Public method to determine, if a REPL can be started.
62
63 @return tuple containing a flag indicating it is safe to start a REPL
64 and a reason why it cannot.
65 @rtype tuple of (bool, str)
66 """
67 return True, ""
68
69 def setRepl(self, on):
70 """
71 Public method to set the REPL status and dependent status.
72
73 @param on flag indicating the active status
74 @type bool
75 """
76 self.__replActive = on
77
78 def canStartPlotter(self):
79 """
80 Public method to determine, if a Plotter can be started.
81
82 @return tuple containing a flag indicating it is safe to start a
83 Plotter and a reason why it cannot.
84 @rtype tuple of (bool, str)
85 """
86 return True, ""
87
88 def setPlotter(self, on):
89 """
90 Public method to set the Plotter status and dependent status.
91
92 @param on flag indicating the active status
93 @type bool
94 """
95 self.__plotterActive = on
96
97 def getWorkspace(self):
98 """
99 Public method to get the workspace directory.
100
101 @return workspace directory used for saving files
102 @rtype str
103 """
104 deviceDirectory = None
105
106 # Attempts to find the path on the filesystem that represents the
107 # plugged in CIRCUITPY board.
108 if Globals.isWindowsPlatform():
109 # we are on a Windows platform
110 def getVolumeName(diskName):
111 """
112 Local function to determine the volume of a disk or device.
113
114 Each disk or external device connected to windows has an
115 attribute called "volume name". This function returns the
116 volume name for the given disk/device.
117
118 Code from http://stackoverflow.com/a/12056414
119 """
120 volumeNameBuffer = ctypes.create_unicode_buffer(1024)
121 ctypes.windll.kernel32.GetVolumeInformationW(
122 ctypes.c_wchar_p(diskName), volumeNameBuffer,
123 ctypes.sizeof(volumeNameBuffer), None, None, None, None, 0)
124 return volumeNameBuffer.value
125
126 #
127 # In certain circumstances, volumes are allocated to USB
128 # storage devices which cause a Windows popup to raise if their
129 # volume contains no media. Wrapping the check in SetErrorMode
130 # with SEM_FAILCRITICALERRORS (1) prevents this popup.
131 #
132 oldMode = ctypes.windll.kernel32.SetErrorMode(1)
133 try:
134 for disk in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ':
135 path = '{}:\\'.format(disk)
136 if (os.path.exists(path) and
137 getVolumeName(path) == 'CIRCUITPY'):
138 return path
139 finally:
140 ctypes.windll.kernel32.SetErrorMode(oldMode)
141 else:
142 # we are on a Linux or macOS platform
143 for mountCommand in ['mount', '/sbin/mount']:
144 try:
145 mountOutput = check_output(mountCommand).splitlines()
146 mountedVolumes = [x.split()[2] for x in mountOutput]
147 for volume in mountedVolumes:
148 if volume.endswith(b'CIRCUITPY'):
149 deviceDirectory = volume.decode('utf-8')
150 except FileNotFoundError:
151 next
152
153 if deviceDirectory:
154 return deviceDirectory
155 else:
156 # return the default workspace and give the user a warning
157 E5MessageBox.warning(
158 self.microPythonWidget,
159 self.tr("Workspace Directory"),
160 self.tr("Python files for CircuitPython devices are stored on"
161 " the device. Therefore, to edit these files you need"
162 " to have the device plugged in. Until you plug in a"
163 " device, the standard directory will be used."))
164
165 return super(CircuitPythonDevice, self).getWorkspace()

eric ide

mercurial