src/eric7/MicroPython/CircuitPythonDevices.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9209
b99e7fd55fd3
child 9413
80c06d472826
equal deleted inserted replaced
9220:e9e7eca7efee 9221:bf71ee032bb4
23 23
24 class CircuitPythonDevice(MicroPythonDevice): 24 class CircuitPythonDevice(MicroPythonDevice):
25 """ 25 """
26 Class implementing the device for CircuitPython boards. 26 Class implementing the device for CircuitPython boards.
27 """ 27 """
28
28 DeviceVolumeName = "CIRCUITPY" 29 DeviceVolumeName = "CIRCUITPY"
29 30
30 def __init__(self, microPythonWidget, deviceType, parent=None): 31 def __init__(self, microPythonWidget, deviceType, parent=None):
31 """ 32 """
32 Constructor 33 Constructor
33 34
34 @param microPythonWidget reference to the main MicroPython widget 35 @param microPythonWidget reference to the main MicroPython widget
35 @type MicroPythonWidget 36 @type MicroPythonWidget
36 @param deviceType device type assigned to this device interface 37 @param deviceType device type assigned to this device interface
37 @type str 38 @type str
38 @param parent reference to the parent object 39 @param parent reference to the parent object
39 @type QObject 40 @type QObject
40 """ 41 """
41 super().__init__( 42 super().__init__(microPythonWidget, deviceType, parent)
42 microPythonWidget, deviceType, parent) 43
43
44 self.__workspace = self.__findWorkspace() 44 self.__workspace = self.__findWorkspace()
45 45
46 self.__nonUF2devices = { 46 self.__nonUF2devices = {
47 "teensy": self.__flashTeensy, 47 "teensy": self.__flashTeensy,
48 } 48 }
49 49
50 def setButtons(self): 50 def setButtons(self):
51 """ 51 """
52 Public method to enable the supported action buttons. 52 Public method to enable the supported action buttons.
53 """ 53 """
54 super().setButtons() 54 super().setButtons()
55 self.microPython.setActionButtons( 55 self.microPython.setActionButtons(
56 run=True, repl=True, files=True, chart=HAS_QTCHART) 56 run=True, repl=True, files=True, chart=HAS_QTCHART
57 57 )
58
58 if self.__deviceVolumeMounted(): 59 if self.__deviceVolumeMounted():
59 self.microPython.setActionButtons(open=True, save=True) 60 self.microPython.setActionButtons(open=True, save=True)
60 61
61 def forceInterrupt(self): 62 def forceInterrupt(self):
62 """ 63 """
63 Public method to determine the need for an interrupt when opening the 64 Public method to determine the need for an interrupt when opening the
64 serial connection. 65 serial connection.
65 66
66 @return flag indicating an interrupt is needed 67 @return flag indicating an interrupt is needed
67 @rtype bool 68 @rtype bool
68 """ 69 """
69 return False 70 return False
70 71
71 def deviceName(self): 72 def deviceName(self):
72 """ 73 """
73 Public method to get the name of the device. 74 Public method to get the name of the device.
74 75
75 @return name of the device 76 @return name of the device
76 @rtype str 77 @rtype str
77 """ 78 """
78 return self.tr("CircuitPython") 79 return self.tr("CircuitPython")
79 80
80 def canStartRepl(self): 81 def canStartRepl(self):
81 """ 82 """
82 Public method to determine, if a REPL can be started. 83 Public method to determine, if a REPL can be started.
83 84
84 @return tuple containing a flag indicating it is safe to start a REPL 85 @return tuple containing a flag indicating it is safe to start a REPL
85 and a reason why it cannot. 86 and a reason why it cannot.
86 @rtype tuple of (bool, str) 87 @rtype tuple of (bool, str)
87 """ 88 """
88 return True, "" 89 return True, ""
89 90
90 def canStartPlotter(self): 91 def canStartPlotter(self):
91 """ 92 """
92 Public method to determine, if a Plotter can be started. 93 Public method to determine, if a Plotter can be started.
93 94
94 @return tuple containing a flag indicating it is safe to start a 95 @return tuple containing a flag indicating it is safe to start a
95 Plotter and a reason why it cannot. 96 Plotter and a reason why it cannot.
96 @rtype tuple of (bool, str) 97 @rtype tuple of (bool, str)
97 """ 98 """
98 return True, "" 99 return True, ""
99 100
100 def canRunScript(self): 101 def canRunScript(self):
101 """ 102 """
102 Public method to determine, if a script can be executed. 103 Public method to determine, if a script can be executed.
103 104
104 @return tuple containing a flag indicating it is safe to start a 105 @return tuple containing a flag indicating it is safe to start a
105 Plotter and a reason why it cannot. 106 Plotter and a reason why it cannot.
106 @rtype tuple of (bool, str) 107 @rtype tuple of (bool, str)
107 """ 108 """
108 return True, "" 109 return True, ""
109 110
110 def runScript(self, script): 111 def runScript(self, script):
111 """ 112 """
112 Public method to run the given Python script. 113 Public method to run the given Python script.
113 114
114 @param script script to be executed 115 @param script script to be executed
115 @type str 116 @type str
116 """ 117 """
117 pythonScript = script.split("\n") 118 pythonScript = script.split("\n")
118 self.sendCommands(pythonScript) 119 self.sendCommands(pythonScript)
119 120
120 def canStartFileManager(self): 121 def canStartFileManager(self):
121 """ 122 """
122 Public method to determine, if a File Manager can be started. 123 Public method to determine, if a File Manager can be started.
123 124
124 @return tuple containing a flag indicating it is safe to start a 125 @return tuple containing a flag indicating it is safe to start a
125 File Manager and a reason why it cannot. 126 File Manager and a reason why it cannot.
126 @rtype tuple of (bool, str) 127 @rtype tuple of (bool, str)
127 """ 128 """
128 return True, "" 129 return True, ""
129 130
130 def supportsLocalFileAccess(self): 131 def supportsLocalFileAccess(self):
131 """ 132 """
132 Public method to indicate file access via a local directory. 133 Public method to indicate file access via a local directory.
133 134
134 @return flag indicating file access via local directory 135 @return flag indicating file access via local directory
135 @rtype bool 136 @rtype bool
136 """ 137 """
137 return self.__deviceVolumeMounted() 138 return self.__deviceVolumeMounted()
138 139
139 def __deviceVolumeMounted(self): 140 def __deviceVolumeMounted(self):
140 """ 141 """
141 Private method to check, if the device volume is mounted. 142 Private method to check, if the device volume is mounted.
142 143
143 @return flag indicated a mounted device 144 @return flag indicated a mounted device
144 @rtype bool 145 @rtype bool
145 """ 146 """
146 if self.__workspace and not os.path.exists(self.__workspace): 147 if self.__workspace and not os.path.exists(self.__workspace):
147 self.__workspace = "" # reset 148 self.__workspace = "" # reset
148 149
149 return self.DeviceVolumeName in self.getWorkspace(silent=True) 150 return self.DeviceVolumeName in self.getWorkspace(silent=True)
150 151
151 def getWorkspace(self, silent=False): 152 def getWorkspace(self, silent=False):
152 """ 153 """
153 Public method to get the workspace directory. 154 Public method to get the workspace directory.
154 155
155 @param silent flag indicating silent operations 156 @param silent flag indicating silent operations
156 @type bool 157 @type bool
157 @return workspace directory used for saving files 158 @return workspace directory used for saving files
158 @rtype str 159 @rtype str
159 """ 160 """
161 # return cached entry 162 # return cached entry
162 return self.__workspace 163 return self.__workspace
163 else: 164 else:
164 self.__workspace = self.__findWorkspace(silent=silent) 165 self.__workspace = self.__findWorkspace(silent=silent)
165 return self.__workspace 166 return self.__workspace
166 167
167 def __findWorkspace(self, silent=False): 168 def __findWorkspace(self, silent=False):
168 """ 169 """
169 Private method to find the workspace directory. 170 Private method to find the workspace directory.
170 171
171 @param silent flag indicating silent operations 172 @param silent flag indicating silent operations
172 @type bool 173 @type bool
173 @return workspace directory used for saving files 174 @return workspace directory used for saving files
174 @rtype str 175 @rtype str
175 """ 176 """
176 # Attempts to find the paths on the filesystem that represents the 177 # Attempts to find the paths on the filesystem that represents the
177 # plugged in CIRCUITPY boards. 178 # plugged in CIRCUITPY boards.
178 deviceDirectories = Utilities.findVolume(self.DeviceVolumeName, 179 deviceDirectories = Utilities.findVolume(self.DeviceVolumeName, findAll=True)
179 findAll=True) 180
180
181 if deviceDirectories: 181 if deviceDirectories:
182 if len(deviceDirectories) == 1: 182 if len(deviceDirectories) == 1:
183 return deviceDirectories[0] 183 return deviceDirectories[0]
184 else: 184 else:
185 return self.selectDeviceDirectory(deviceDirectories) 185 return self.selectDeviceDirectory(deviceDirectories)
188 # silent mode is selected) 188 # silent mode is selected)
189 if not silent: 189 if not silent:
190 EricMessageBox.warning( 190 EricMessageBox.warning(
191 self.microPython, 191 self.microPython,
192 self.tr("Workspace Directory"), 192 self.tr("Workspace Directory"),
193 self.tr("Python files for CircuitPython can be edited in" 193 self.tr(
194 " place, if the device volume is locally" 194 "Python files for CircuitPython can be edited in"
195 " available. Such a volume was not found. In" 195 " place, if the device volume is locally"
196 " place editing will not be available." 196 " available. Such a volume was not found. In"
197 ) 197 " place editing will not be available."
198 ),
198 ) 199 )
199 200
200 return super().getWorkspace() 201 return super().getWorkspace()
201 202
202 def addDeviceMenuEntries(self, menu): 203 def addDeviceMenuEntries(self, menu):
203 """ 204 """
204 Public method to add device specific entries to the given menu. 205 Public method to add device specific entries to the given menu.
205 206
206 @param menu reference to the context menu 207 @param menu reference to the context menu
207 @type QMenu 208 @type QMenu
208 """ 209 """
209 connected = self.microPython.isConnected() 210 connected = self.microPython.isConnected()
210 211
211 act = menu.addAction(self.tr("Flash CircuitPython Firmware"), 212 act = menu.addAction(
212 self.__flashCircuitPython) 213 self.tr("Flash CircuitPython Firmware"), self.__flashCircuitPython
214 )
213 act.setEnabled(not connected) 215 act.setEnabled(not connected)
214 menu.addSeparator() 216 menu.addSeparator()
215 act = menu.addAction(self.tr("Install Library Files"), 217 act = menu.addAction(
216 self.__installLibraryFiles) 218 self.tr("Install Library Files"), self.__installLibraryFiles
219 )
217 act.setEnabled(self.__deviceVolumeMounted()) 220 act.setEnabled(self.__deviceVolumeMounted())
218 221
219 def hasFlashMenuEntry(self): 222 def hasFlashMenuEntry(self):
220 """ 223 """
221 Public method to check, if the device has its own flash menu entry. 224 Public method to check, if the device has its own flash menu entry.
222 225
223 @return flag indicating a specific flash menu entry 226 @return flag indicating a specific flash menu entry
224 @rtype bool 227 @rtype bool
225 """ 228 """
226 return True 229 return True
227 230
228 @pyqtSlot() 231 @pyqtSlot()
229 def __flashCircuitPython(self): 232 def __flashCircuitPython(self):
230 """ 233 """
231 Private slot to flash a CircuitPython firmware to the device. 234 Private slot to flash a CircuitPython firmware to the device.
232 """ 235 """
236 if name in lBoardName: 239 if name in lBoardName:
237 self.__nonUF2devices[name]() 240 self.__nonUF2devices[name]()
238 break 241 break
239 else: 242 else:
240 from .UF2FlashDialog import UF2FlashDialog 243 from .UF2FlashDialog import UF2FlashDialog
244
241 dlg = UF2FlashDialog(boardType="circuitpython") 245 dlg = UF2FlashDialog(boardType="circuitpython")
242 dlg.exec() 246 dlg.exec()
243 247
244 def __flashTeensy(self): 248 def __flashTeensy(self):
245 """ 249 """
246 Private method to show a message box because Teens does not support 250 Private method to show a message box because Teens does not support
247 the UF2 bootloader yet. 251 the UF2 bootloader yet.
248 """ 252 """
249 EricMessageBox.information( 253 EricMessageBox.information(
250 self.microPython, 254 self.microPython,
251 self.tr("Flash CircuitPython Firmware"), 255 self.tr("Flash CircuitPython Firmware"),
252 self.tr("""<p>Teensy 4.0 and Teensy 4.1 do not support the UF2""" 256 self.tr(
253 """ bootloader. Please use the 'Teensy Loader'""" 257 """<p>Teensy 4.0 and Teensy 4.1 do not support the UF2"""
254 """ application to flash CircuitPython. Make sure you""" 258 """ bootloader. Please use the 'Teensy Loader'"""
255 """ downloaded the CircuitPython .hex file.</p>""" 259 """ application to flash CircuitPython. Make sure you"""
256 """<p>See <a href="{0}">the PJRC Teensy web site</a>""" 260 """ downloaded the CircuitPython .hex file.</p>"""
257 """ for details.</p>""") 261 """<p>See <a href="{0}">the PJRC Teensy web site</a>"""
258 .format("https://www.pjrc.com/teensy/loader.html")) 262 """ for details.</p>"""
259 263 ).format("https://www.pjrc.com/teensy/loader.html"),
264 )
265
260 @pyqtSlot() 266 @pyqtSlot()
261 def __installLibraryFiles(self): 267 def __installLibraryFiles(self):
262 """ 268 """
263 Private slot to install Python files into the onboard library. 269 Private slot to install Python files into the onboard library.
264 """ 270 """
265 if not self.__deviceVolumeMounted(): 271 if not self.__deviceVolumeMounted():
266 EricMessageBox.critical( 272 EricMessageBox.critical(
267 self.microPython, 273 self.microPython,
268 self.tr("Install Library Files"), 274 self.tr("Install Library Files"),
269 self.tr("""The device volume "<b>{0}</b>" is not available.""" 275 self.tr(
270 """ Ensure it is mounted properly and try again.""")) 276 """The device volume "<b>{0}</b>" is not available."""
277 """ Ensure it is mounted properly and try again."""
278 ),
279 )
271 return 280 return
272 281
273 target = os.path.join(self.getWorkspace(), "lib") 282 target = os.path.join(self.getWorkspace(), "lib")
274 # ensure that the library directory exists on the device 283 # ensure that the library directory exists on the device
275 if not os.path.isdir(target): 284 if not os.path.isdir(target):
276 os.makedirs(target) 285 os.makedirs(target)
277 286
278 libraryFiles = EricFileDialog.getOpenFileNames( 287 libraryFiles = EricFileDialog.getOpenFileNames(
279 self.microPython, 288 self.microPython,
280 self.tr("Install Library Files"), 289 self.tr("Install Library Files"),
281 os.path.expanduser("~"), 290 os.path.expanduser("~"),
282 self.tr("Compiled Python Files (*.mpy);;" 291 self.tr(
283 "Python Files (*.py);;" 292 "Compiled Python Files (*.mpy);;"
284 "All Files (*)")) 293 "Python Files (*.py);;"
285 294 "All Files (*)"
295 ),
296 )
297
286 for libraryFile in libraryFiles: 298 for libraryFile in libraryFiles:
287 if os.path.exists(libraryFile): 299 if os.path.exists(libraryFile):
288 shutil.copy2(libraryFile, target) 300 shutil.copy2(libraryFile, target)
289 301
290 def getDocumentationUrl(self): 302 def getDocumentationUrl(self):
291 """ 303 """
292 Public method to get the device documentation URL. 304 Public method to get the device documentation URL.
293 305
294 @return documentation URL of the device 306 @return documentation URL of the device
295 @rtype str 307 @rtype str
296 """ 308 """
297 return Preferences.getMicroPython("CircuitPythonDocuUrl") 309 return Preferences.getMicroPython("CircuitPythonDocuUrl")
298 310
299 def getDownloadMenuEntries(self): 311 def getDownloadMenuEntries(self):
300 """ 312 """
301 Public method to retrieve the entries for the downloads menu. 313 Public method to retrieve the entries for the downloads menu.
302 314
303 @return list of tuples with menu text and URL to be opened for each 315 @return list of tuples with menu text and URL to be opened for each
304 entry 316 entry
305 @rtype list of tuple of (str, str) 317 @rtype list of tuple of (str, str)
306 """ 318 """
307 return [ 319 return [
308 (self.tr("CircuitPython Firmware"), 320 (
309 Preferences.getMicroPython("CircuitPythonFirmwareUrl")), 321 self.tr("CircuitPython Firmware"),
310 (self.tr("CircuitPython Libraries"), 322 Preferences.getMicroPython("CircuitPythonFirmwareUrl"),
311 Preferences.getMicroPython("CircuitPythonLibrariesUrl")) 323 ),
324 (
325 self.tr("CircuitPython Libraries"),
326 Preferences.getMicroPython("CircuitPythonLibrariesUrl"),
327 ),
312 ] 328 ]

eric ide

mercurial