src/eric7/MicroPython/CircuitPythonDevices.py

branch
eric7
changeset 9738
4ae976ee5339
parent 9653
e67609152c5e
child 9740
90072e10ae9b
equal deleted inserted replaced
9737:5e05b2089daf 9738:4ae976ee5339
9 9
10 import os 10 import os
11 import shutil 11 import shutil
12 12
13 from PyQt6.QtCore import pyqtSlot 13 from PyQt6.QtCore import pyqtSlot
14 from PyQt6.QtWidgets import QMenu
14 15
15 from eric7 import Preferences 16 from eric7 import Preferences
16 from eric7.EricWidgets import EricFileDialog, EricMessageBox 17 from eric7.EricWidgets import EricFileDialog, EricMessageBox
17 from eric7.SystemUtilities import FileSystemUtilities 18 from eric7.SystemUtilities import FileSystemUtilities
18 19
25 Class implementing the device for CircuitPython boards. 26 Class implementing the device for CircuitPython boards.
26 """ 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, boardName, 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
39 @param boardName name of the board
40 @type str
38 @param parent reference to the parent object 41 @param parent reference to the parent object
39 @type QObject 42 @type QObject
40 """ 43 """
41 super().__init__(microPythonWidget, deviceType, parent) 44 super().__init__(microPythonWidget, deviceType, parent)
42 45
46 self.__boardName = boardName
43 self.__workspace = self.__findWorkspace() 47 self.__workspace = self.__findWorkspace()
44 48
45 self.__nonUF2devices = { 49 self.__nonUF2devices = {
46 "teensy": self.__flashTeensy, 50 "teensy": self.__flashTeensy,
47 } 51 }
162 return self.__workspace 166 return self.__workspace
163 else: 167 else:
164 self.__workspace = self.__findWorkspace(silent=silent) 168 self.__workspace = self.__findWorkspace(silent=silent)
165 return self.__workspace 169 return self.__workspace
166 170
171 def __findDeviceDirectories(self, directories):
172 """
173 Private method to find the device directories associated with the
174 current board name.
175
176 @param directories list of directories to be checked
177 @type list of str
178 @return list of associated directories
179 @rtype list of str
180 """
181 boardDirectories = []
182 for directory in directories:
183 bootFile = os.path.join(directory, "boot_out.txt")
184 if os.path.exists(bootFile):
185 with open(bootFile, "r") as f:
186 line = f.readline()
187 if self.__boardName in line:
188 boardDirectories.append(directory)
189
190 return boardDirectories
191
167 def __findWorkspace(self, silent=False): 192 def __findWorkspace(self, silent=False):
168 """ 193 """
169 Private method to find the workspace directory. 194 Private method to find the workspace directory.
170 195
171 @param silent flag indicating silent operations 196 @param silent flag indicating silent operations
181 206
182 if deviceDirectories: 207 if deviceDirectories:
183 if len(deviceDirectories) == 1: 208 if len(deviceDirectories) == 1:
184 return deviceDirectories[0] 209 return deviceDirectories[0]
185 else: 210 else:
186 return self.selectDeviceDirectory(deviceDirectories) 211 boardDirectories = self.__findDeviceDirectories(deviceDirectories)
212 if len(boardDirectories) == 1:
213 return boardDirectories[0]
214 elif len(boardDirectories) > 1:
215 return self.selectDeviceDirectory(boardDirectories)
216 else:
217 return self.selectDeviceDirectory(deviceDirectories)
187 else: 218 else:
188 # return the default workspace and give the user a warning (unless 219 # return the default workspace and give the user a warning (unless
189 # silent mode is selected) 220 # silent mode is selected)
190 if not silent: 221 if not silent:
191 EricMessageBox.warning( 222 EricMessageBox.warning(
208 @param menu reference to the context menu 239 @param menu reference to the context menu
209 @type QMenu 240 @type QMenu
210 """ 241 """
211 connected = self.microPython.isConnected() 242 connected = self.microPython.isConnected()
212 243
244 self.__libraryMenu = QMenu(self.tr("Library Management"))
245 act = self.__libraryMenu.addAction(
246 self.tr("Install Library Files"), self.__installLibraryFiles
247 )
248 act.setEnabled(self.__deviceVolumeMounted())
249 act = self.__libraryMenu.addAction(
250 self.tr("Install Library Package"),
251 lambda: self.__installLibraryFiles(packageMode=True),
252 )
253 act.setEnabled(self.__deviceVolumeMounted())
254
213 act = menu.addAction( 255 act = menu.addAction(
214 self.tr("Flash CircuitPython Firmware"), self.__flashCircuitPython 256 self.tr("Flash CircuitPython Firmware"), self.__flashCircuitPython
215 ) 257 )
216 act.setEnabled(not connected) 258 act.setEnabled(not connected)
217 menu.addSeparator() 259 menu.addSeparator()
218 act = menu.addAction( 260 menu.addMenu(self.__libraryMenu)
219 self.tr("Install Library Files"), self.__installLibraryFiles
220 )
221 act.setEnabled(self.__deviceVolumeMounted())
222 261
223 def hasFlashMenuEntry(self): 262 def hasFlashMenuEntry(self):
224 """ 263 """
225 Public method to check, if the device has its own flash menu entry. 264 Public method to check, if the device has its own flash menu entry.
226 265
263 """ for details.</p>""" 302 """ for details.</p>"""
264 ).format("https://www.pjrc.com/teensy/loader.html"), 303 ).format("https://www.pjrc.com/teensy/loader.html"),
265 ) 304 )
266 305
267 @pyqtSlot() 306 @pyqtSlot()
268 def __installLibraryFiles(self): 307 def __installLibraryFiles(self, packageMode=False):
269 """ 308 """
270 Private slot to install Python files into the onboard library. 309 Private slot to install Python files into the onboard library.
271 """ 310
311 @param packageMode flag indicating to install a library package
312 (defaults to False)
313 @type bool (optional)
314 """
315 title = (
316 self.tr("Install Library Package")
317 if packageMode
318 else self.tr("Install Library Files")
319 )
272 if not self.__deviceVolumeMounted(): 320 if not self.__deviceVolumeMounted():
273 EricMessageBox.critical( 321 EricMessageBox.critical(
274 self.microPython, 322 self.microPython,
275 self.tr("Install Library Files"), 323 title,
276 self.tr( 324 self.tr(
277 """The device volume "<b>{0}</b>" is not available.""" 325 """The device volume "<b>{0}</b>" is not available."""
278 """ Ensure it is mounted properly and try again.""" 326 """ Ensure it is mounted properly and try again."""
279 ), 327 ),
280 ) 328 )
283 target = os.path.join(self.getWorkspace(), "lib") 331 target = os.path.join(self.getWorkspace(), "lib")
284 # ensure that the library directory exists on the device 332 # ensure that the library directory exists on the device
285 if not os.path.isdir(target): 333 if not os.path.isdir(target):
286 os.makedirs(target) 334 os.makedirs(target)
287 335
288 libraryFiles = EricFileDialog.getOpenFileNames( 336 if packageMode:
289 self.microPython, 337 libraryPackage = EricFileDialog.getExistingDirectory(
290 self.tr("Install Library Files"), 338 self.microPython,
291 os.path.expanduser("~"), 339 title,
292 self.tr( 340 os.path.expanduser("~"),
293 "Compiled Python Files (*.mpy);;" 341 EricFileDialog.Option(0),
294 "Python Files (*.py);;" 342 )
295 "All Files (*)" 343 if libraryPackage:
296 ), 344 target = os.path.join(target, os.path.basename(libraryPackage))
297 ) 345 shutil.rmtree(target, ignore_errors=True)
298 346 shutil.copytree(libraryPackage, target)
299 for libraryFile in libraryFiles: 347 else:
300 if os.path.exists(libraryFile): 348 libraryFiles = EricFileDialog.getOpenFileNames(
301 shutil.copy2(libraryFile, target) 349 self.microPython,
350 title,
351 os.path.expanduser("~"),
352 self.tr(
353 "Compiled Python Files (*.mpy);;"
354 "Python Files (*.py);;"
355 "All Files (*)"
356 ),
357 )
358
359 for libraryFile in libraryFiles:
360 if os.path.exists(libraryFile):
361 shutil.copy2(libraryFile, target)
302 362
303 def getDocumentationUrl(self): 363 def getDocumentationUrl(self):
304 """ 364 """
305 Public method to get the device documentation URL. 365 Public method to get the device documentation URL.
306 366
327 Preferences.getMicroPython("CircuitPythonLibrariesUrl"), 387 Preferences.getMicroPython("CircuitPythonLibrariesUrl"),
328 ), 388 ),
329 ] 389 ]
330 390
331 391
332 def createDevice(microPythonWidget, deviceType, vid, pid): 392 def createDevice(microPythonWidget, deviceType, vid, pid, boardName):
333 """ 393 """
334 Function to instantiate a MicroPython device object. 394 Function to instantiate a MicroPython device object.
335 395
336 @param microPythonWidget reference to the main MicroPython widget 396 @param microPythonWidget reference to the main MicroPython widget
337 @type MicroPythonWidget 397 @type MicroPythonWidget
339 @type str 399 @type str
340 @param vid vendor ID 400 @param vid vendor ID
341 @type int 401 @type int
342 @param pid product ID 402 @param pid product ID
343 @type int 403 @type int
404 @param boardName name of the board
405 @type str
344 @return reference to the instantiated device object 406 @return reference to the instantiated device object
345 @rtype CircuitPythonDevice 407 @rtype CircuitPythonDevice
346 """ 408 """
347 return CircuitPythonDevice(microPythonWidget, deviceType) 409 return CircuitPythonDevice(microPythonWidget, deviceType, boardName)

eric ide

mercurial