eric6/MicroPython/UF2FlashDialog.py

changeset 8096
5425a9072300
child 8097
5af9c426c46b
equal deleted inserted replaced
8095:d8caff84ffcf 8096:5425a9072300
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2021 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a dialog to flash any UF2 capable device.
8 """
9
10 import os
11 import shutil
12
13 from PyQt5.QtCore import pyqtSlot, Qt, QCoreApplication, QThread, QEventLoop
14 from PyQt5.QtWidgets import QDialog
15
16 from E5Gui.E5PathPicker import E5PathPickerModes
17
18 from .Ui_UF2FlashDialog import Ui_UF2FlashDialog
19
20 import UI.PixmapCache
21 import Utilities
22
23 from . import MicroPythonDevices
24
25 SupportedUF2Boards = {
26 "circuitpython": {
27 "volumes": {
28 (0x03EB, 0x2402): [
29 "SAMD21", # SAMD21 Board
30 "SAME54", # SAME54 Board
31 ],
32 (0x04D8, 0xEC44): [
33 "PYCUBEDBOOT", # PyCubedv04
34 ],
35 (0x04D8, 0xEC63): [
36 "BOOT", # CircuitBrains Basic
37 ],
38 (0x04D8, 0xEC64): [
39 "BOOT", # CircuitBrains Deluxe
40 ],
41 (0x04D8, 0xED5F): [
42 "UCHIPYBOOT", # uChip CircuitPython
43 ],
44 (0x04D8, 0xEDB3): [
45 "USBHUBBOOT", # Programmable USB Hub
46 ],
47 (0x04D8, 0xEDBE): [
48 "SAM32BOOT", # SAM32
49 ],
50 (0x04D8, 0xEF66): [
51 "SENSEBOX", # senseBox MCU
52 ],
53 (0x1209, 0x2017): [
54 "MINISAMBOOT", # Mini SAM M4
55 ],
56 (0x1209, 0x4D44): [
57 "ROBOM0BOOT", # Robo HAT MM1
58 "ROBOM4BOOT", # Robo HAT MM1 M4
59 ],
60 (0x1209, 0x4DDD): [
61 "SapBOOT", # CP Sapling
62 ],
63 (0x1209, 0x7102): [
64 "MINISAMBOOT", # Mini SAM M0
65 ],
66 (0x1209, 0x805A): [
67 "BASTBLE", # Bast BLE
68 ],
69 (0x1209, 0xE3E2): [
70 "StackRduino", # StackRduino M0 PRO
71 ],
72 (0x1209, 0xF501): [
73 "M4SHIMBOOT", # M4-Shim
74 ],
75 (0x16D0, 0x0CDA): [
76 "AUTOMAT", # automat
77 ],
78 (0x1B4F, 0x0019): [
79 "QwiicMicro", # Sparkfun Qwiic Micro
80 ],
81 (0x1B4F, 0x0D22): [
82 "SPARKFUN", # Sparkfun SAMD21 Mini Breakout
83 ],
84 (0x1B4F, 0x0D23): [
85 "SPARKFUN", # Sparkfun SAMD21 Dev Breakout
86 ],
87 (0x1D50, 0x6110): [
88 "ROBOTICS", # Robotics
89 ],
90 (0x1D50, 0x6112): [
91 "RCBOOT", # Wattuino RC
92 ],
93 (0x1D50, 0x6160): [
94 "BLUEMICRO", # BlueMicro
95 ],
96 (0x230A, 0x00E9): [
97 "TAU_BOOT", # Tau
98 ],
99 (0x2341, 0x0057): [
100 "NANOBOOT", # NANO 33 IoT
101 ],
102 (0x2341, 0x8053): [
103 "MKR1300", # MKR1300
104 ],
105 (0x239A, 0x000F): [
106 "ITSYBOOT", # ItsyBitsy M0 Express
107 ],
108 (0x239A, 0x0013): [
109 "METROBOOT", # Metro M0
110 ],
111 (0x239A, 0x0015): [
112 "FEATHERBOOT", # Feather M0
113 ],
114 (0x239A, 0x0018): [
115 "CPLAYBOOT", # CPlay Express
116 ],
117 (0x239A, 0x001B): [
118 "FEATHERBOOT", # Feather M0 Express
119 ],
120 (0x239A, 0x001C): [
121 "GEMMABOOT", # Gemma M0
122 ],
123 (0x239A, 0x001E): [
124 "TRINKETBOOT", # Trinket M0
125 ],
126 (0x239A, 0x0021): [
127 "METROM4BOOT", # Metro M4 Express
128 ],
129 (0x239A, 0x0022): [
130 "ARCADE-D5", # Feather Arcade D51
131 "FEATHERBOOT", # Feather M4 Express
132 ],
133 (0x239A, 0x0024): [
134 "RADIOBOOT", # Radiofruit M0
135 ],
136 (0x239A, 0x0027): [
137 "PIRKEYBOOT", # pIRKey M0
138 ],
139 (0x239A, 0x0029): [
140 "ARGONBOOT ", # Argon
141 "BORONBOOT ", # Boron
142 "FTHR840BOOT", # Feather nRF52840 Express
143 "MDBT50QBOOT", # Raytac MDBT50Q-RX
144 "MDK840DONGL", # MDK nRF52840 USB Dongle
145 "WS52840EVK", # Waveshare nRF52840 Eval
146 "XENONBOOT ", # Xenon
147 ],
148 (0x239A, 0x002B): [
149 "ARCADE-D5", # Itsy Arcade D51
150 "ITSYM4BOOT", # ItsyBitsy M4 Express
151 ],
152 (0x239A, 0x002D): [
153 "CRICKITBOOT", # crickit
154 ],
155 (0x239A, 0x002F): [
156 "TRELM4BOOT", # Trellis M4 Express
157 ],
158 (0x239A, 0x0031): [
159 "GCM4BOOT", # Grand Central M4 Express
160 ],
161 (0x239A, 0x0033): [
162 "PYBADGEBOOT", # PyBadge
163 ],
164 (0x239A, 0x0034): [
165 "BADGELCBOOT", # BadgeLC
166 "PEWBOOT", # PewPew
167 ],
168 (0x239A, 0x0035): [
169 "MKRZEROBOOT", # MKRZero
170 "PORTALBOOT", # PyPortal M4 Express
171 ],
172 (0x239A, 0x0037): [
173 "METROM4BOOT", # Metro M4 AirLift
174 ],
175 (0x239A, 0x003D): [
176 "PYGAMERBOOT", # PyGamer
177 ],
178 (0x239A, 0x003F): [
179 "METR840BOOT", # Metro nRF52840 Express
180 ],
181 (0x239A, 0x0045): [
182 "CPLAYBTBOOT", # Circuit Playground nRF52840
183 ],
184 (0x239A, 0x0047): [
185 "MASKM4BOOT", # Hallowing Mask M4
186 ],
187 (0x239A, 0x0049): [
188 "HALLOM4BOOT", # HalloWing M4
189 ],
190 (0x239A, 0x004D): [
191 "SNEKBOOT", # snekboard
192 ],
193 (0x239A, 0x0051): [
194 "ITSY840BOOT", # ItsyBitsy nRF52840 Express
195 ],
196 (0x239A, 0x0057): [
197 "SERPENTBOOT", # Serpente
198 ],
199 (0x239A, 0x0061): [
200 "SOLBOOT", # Sol
201 ],
202 (0x239A, 0x0063): [
203 "NANO33BOOT", # Nano 33 BLE
204 ],
205 (0x239A, 0x0065): [
206 "ND6BOOT", # ndBit6
207 ],
208 (0x239A, 0x006B): [
209 "shIRtty", # shIRtty
210 ],
211 (0x239A, 0x0071): [
212 "CLUEBOOT", # CLUE nRF52840
213 ],
214 (0x239A, 0x0079): [
215 "ARAMBOOT", # ARAMCON Badge 2019
216 ],
217 (0x239A, 0x007D): [
218 "BOOKBOOT", # The Open Book Feather
219 ],
220 (0x239A, 0x007F): [
221 "BADGEBOOT", # OHS2020 Badge
222 ],
223 (0x239A, 0x0087): [
224 "FTHRSNSBOOT", # Feather nRF52840 Sense
225 ],
226 (0x239A, 0x0093): [
227 "ISVITABoot", # IkigaiSense Vita nRF52840
228 ],
229 (0x239A, 0x0095): [
230 "UARTLOGBOOT", # UARTLogger II
231 ],
232 (0x239A, 0x009F): [
233 "ADM840BOOT", # AtelierDuMaker NRF52840 Breakout
234 ],
235 (0x239A, 0x00AF): [
236 "FLUFFBOOT", # Fluff M0
237 ],
238 (0x239A, 0x00B3): [
239 "NICENANO", # nice!nano
240 ],
241 (0x239A, 0x00B5): [
242 "E54XBOOT", # SAME54 Xplained
243 ],
244 (0x239A, 0x00B9): [
245 "ND7BOOT", # ndBit7
246 ],
247 (0x239A, 0x00BF): [
248 "BADGEBOOT", # BLM Badge
249 ],
250 (0x239A, 0x00C3): [
251 "GEMINIBOOT", # Gemini
252 ],
253 (0x239A, 0x00CB): [
254 "QTPY_BOOT", # QT Py M0
255 ],
256 (0x239A, 0x00CD): [
257 "FTHRCANBOOT", # Feather M4 CAN Express
258 ],
259 (0x239A, 0x00EF): [
260 "TRINKEYBOOT", # NeoPixel Trinkey M0
261 ],
262 (0x239A, 0x00F5): [
263 "STARBOOT", # Binary Star
264 ],
265 (0x239A, 0xB000): [
266 "HALLOWBOOT", # Hallowing M0
267 ],
268 (0x239A, 0xE005): [
269 "HONKBOOT", # Big Honking Button
270 ],
271 (0x2886, 0x000D): [
272 "Grove Zero", # Grove Zero
273 ],
274 (0x2886, 0x002F): [
275 "Seeed XIAO", # Seeeduino XIAO
276 "Arduino", # Seeeduino XIAO
277 ],
278 (0x2886, 0xF00E): [
279 "PITAYAGO", # Pitaya Go
280 ],
281 (0x2886, 0xF00F): [
282 "nRF52840M2", # MakerDiary nRF52840 M.2 Module
283 ],
284 (0x3171, 0x0100): [
285 "CMDBOOT", # COMMANDER
286 ],
287 },
288 "instructions": QCoreApplication.translate(
289 "UF2FlashDialog",
290 "<h3>CircuitPython Board</h3>"
291 "<p>In order to prepare the board for flashing follow these"
292 " steps:</p><ol>"
293 "<li>Switch your device to 'bootloader' mode by double-pressing"
294 " the reset button.</li>"
295 "<li>Wait until the device has entered 'bootloader' mode.</li>"
296 "<li>(If this does not happen, then try shorter or longer"
297 " pauses between presses.)</li>"
298 "<li>Ensure the boot volume is available (this may require"
299 " mounting it).</li>"
300 "<li>Select the firmware file to be flashed and click the"
301 " flash button.</li>"
302 "</ol>"
303 ),
304 "firmware": "CircuitPython",
305 },
306
307 "rp2040": {
308 "volumes": [
309
310 ],
311 "instructions": QCoreApplication.translate(
312 "UF2FlashDialog",
313 "<h3>Pi Pico (RP2040) Board</h3>"
314 "<p>In order to prepare the board for flashing follow these"
315 " steps:</p><ol>"
316 "<li>Plug in your board while holding the BOOTSEL button.</li>"
317 "<li>Wait until the device has entered 'bootloader' mode.</li>"
318 "<li>Ensure the boot volume is available (this may require"
319 " mounting it).</li>"
320 "<li>Select the firmware file to be flashed and click the"
321 " flash button.</li>"
322 "</ol>"
323 ),
324 "firmware": "MicroPython",
325 },
326 }
327
328
329 def getFoundDevices(boardType=""):
330 """
331 Function to get the list of known serial devices supporting UF2.
332
333 @param boardType specific board type to search for
334 @type str
335 @return list of tuples with the board type, the port description, the
336 VID and PID
337 @rtype list of tuple of (str, str, int, int)
338 """
339 from PyQt5.QtSerialPort import QSerialPortInfo
340
341 foundDevices = []
342
343 availablePorts = QSerialPortInfo.availablePorts()
344 for port in availablePorts:
345 vid = port.vendorIdentifier()
346 pid = port.productIdentifier()
347
348 if vid == 0 and pid == 0:
349 # no device detected at port
350 continue
351
352 for board in SupportedUF2Boards:
353 if not boardType or (board == boardType):
354 if (vid, pid) in SupportedUF2Boards[board]["volumes"]:
355 foundDevices.append((
356 board,
357 port.description(),
358 (vid, pid),
359 ))
360
361 return foundDevices
362
363
364 class UF2FlashDialog(QDialog, Ui_UF2FlashDialog):
365 """
366 Class implementing a dialog to flash any UF2 capable device.
367 """
368 DeviceTypeRole = Qt.UserRole
369 DeviceVidPidRole = Qt.UserRole + 1
370
371 def __init__(self, boardType="", parent=None):
372 """
373 Constructor
374
375 @param boardType specific board type to show the dialog for
376 @type str
377 @param parent reference to the parent widget (defaults to None)
378 @type QWidget (optional)
379 """
380 super(UF2FlashDialog, self).__init__(parent)
381 self.setupUi(self)
382
383 self.refreshButton.setIcon(UI.PixmapCache.getIcon("rescan"))
384
385 self.firmwarePicker.setMode(E5PathPickerModes.OpenFileMode)
386 self.firmwarePicker.setFilters(
387 self.tr("MicroPython/CircuitPython Files (*.uf2);;"
388 "All Files (*)"))
389
390 self.bootPicker.setMode(E5PathPickerModes.DirectoryShowFilesMode)
391 self.bootPicker.setEnabled(False)
392
393 self.__mandatoryStyleSheet = (
394 "QLineEdit {border: 2px solid;border-color: #800000}"
395 )
396 self.__manualType = "<manual>"
397
398 self.__boardType = boardType
399
400 self.__populate()
401
402 self.__updateFlashButton()
403
404 def __populate(self):
405 """
406 Private method to (re-)populate the dialog.
407 """
408 # save the currently selected device
409 currentDevice = self.devicesComboBox.currentText()
410 firmwareFile = self.firmwarePicker.text()
411
412 # clear the entries first
413 self.devicesComboBox.clear()
414 self.firmwarePicker.clear()
415 self.bootPicker.clear()
416 self.infoLabel.clear()
417 self.infoEdit.clear()
418
419 # now populate the entries with data
420 devices = getFoundDevices(boardType=self.__boardType)
421 if len(devices) == 0:
422 # no device detected
423 devices = filter(
424 lambda x: x[0] in SupportedUF2Boards,
425 MicroPythonDevices.getFoundDevices()[0]
426 )
427 if devices:
428 self.__showSpecificInstructions(list(devices))
429 else:
430 self.__showAllInstructions()
431 self.devicesComboBox.addItem("")
432 self.devicesComboBox.addItem(self.tr("Manual Select"))
433 self.devicesComboBox.setItemData(1, self.__manualType,
434 self.DeviceTypeRole)
435 elif len(devices) == 1:
436 self.devicesComboBox.addItem(devices[0][1])
437 self.devicesComboBox.setItemData(
438 0, devices[0][0], self.DeviceTypeRole)
439 self.devicesComboBox.setItemData(
440 0, devices[0][2], self.DeviceVidPidRole)
441 self.devicesComboBox.addItem(self.tr("Manual Select"))
442 self.devicesComboBox.setItemData(1, self.__manualType,
443 self.DeviceTypeRole)
444 self.on_devicesComboBox_currentIndexChanged(0)
445 else:
446 self.devicesComboBox.addItem("")
447 for index, (boardType, description,
448 vidpid) in enumerate(sorted(devices), 1):
449 self.devicesComboBox.addItem(description)
450 self.devicesComboBox.setItemData(
451 index, boardType, self.DeviceTypeRole)
452 self.devicesComboBox.setItemData(
453 index, vidpid, self.DeviceVidPidRole)
454 self.devicesComboBox.addItem(self.tr("Manual Select"))
455 self.devicesComboBox.setItemData(index + 1, self.__manualType,
456 self.DeviceTypeRole)
457
458 # reselect the remembered device, if it is still there
459 if currentDevice:
460 self.devicesComboBox.setCurrentText(currentDevice)
461 self.firmwarePicker.setText(firmwareFile)
462 else:
463 self.devicesComboBox.setCurrentIndex(0)
464
465 def __updateFlashButton(self):
466 """
467 Private method to update the state of the Flash button and the retest
468 button.
469 """
470 firmwareFile = self.firmwarePicker.text()
471 if self.devicesComboBox.currentData(self.DeviceTypeRole) is not None:
472 if bool(firmwareFile) and os.path.exists(firmwareFile):
473 self.firmwarePicker.setStyleSheet("")
474 else:
475 self.firmwarePicker.setStyleSheet(self.__mandatoryStyleSheet)
476
477 if bool(self.bootPicker.text()):
478 self.bootPicker.setStyleSheet("")
479 else:
480 self.bootPicker.setStyleSheet(self.__mandatoryStyleSheet)
481 else:
482 self.firmwarePicker.setStyleSheet("")
483 self.bootPicker.setStyleSheet("")
484
485 enable = (
486 bool(self.bootPicker.text()) and
487 bool(firmwareFile) and
488 os.path.exists(firmwareFile)
489 )
490 self.flashButton.setEnabled(enable)
491
492 def __showAllInstructions(self):
493 """
494 Private method to show instructions for resetting devices to bootloader
495 mode.
496 """
497 self.infoLabel.setText(self.tr("Reset Instructions:"))
498
499 htmlText = self.tr(
500 "<h4>No known devices detected.</h4>"
501 "<p>Follow the appropriate instructions below to set <b>one</b>"
502 " board into 'bootloader' mode. Press <b>Refresh</b> when ready."
503 "</p>"
504 )
505 for boardType in SupportedUF2Boards:
506 htmlText += "<hr/>" + SupportedUF2Boards[boardType]["instructions"]
507 self.infoEdit.setHtml(htmlText)
508
509 def __showSpecificInstructions(self, devices):
510 """
511 Private method to show instructions for resetting devices to bootloader
512 mode for a list of detected devices.
513
514 @param devices list of detected devices
515 @type list of str
516 """
517 boardTypes = set(x[0] for x in devices)
518
519 self.infoLabel.setText(self.tr("Reset Instructions:"))
520
521 if self.__boardType:
522 htmlText = self.tr(
523 "<h4>Flash {0} Firmware</h4>"
524 "<p>Follow the instructions below to set <b>one</b> board into"
525 " 'bootloader' mode. Press <b>Refresh</b> when ready.</p>"
526 "<hr/>{1}"
527 ).format(
528 SupportedUF2Boards[self.__boardType]["firmware"],
529 SupportedUF2Boards[self.__boardType]["instructions"],
530 )
531 else:
532 htmlText = self.tr(
533 "<h4>Potentially UF2 capable devices found</h4>"
534 "<p>Found these potentially UF2 capable devices:</p>"
535 "<ul><li>{0}</li></ul>"
536 "<p>Follow the instructions below to set <b>one</b> board into"
537 " 'bootloader' mode. Press <b>Refresh</b> when ready.</p>"
538 ).format(
539 "</li><li>".join(sorted(x[1] for x in devices))
540 )
541 for boardType in sorted(boardTypes):
542 htmlText += (
543 "<hr/>" + SupportedUF2Boards[boardType]["instructions"]
544 )
545 self.infoEdit.setHtml(htmlText)
546
547 def __showTypedInstructions(self, boardType):
548 """
549 Private method to show instructions for resetting devices to bootloader
550 mode for a specific board type.
551
552 @param boardType type of the board to show instructions for
553 @type str
554 """
555 self.infoLabel.setText(self.tr("Reset Instructions:"))
556
557 htmlText = self.tr(
558 "<h4>No known devices detected.</h4>"
559 "<p>Follow the instructions below to set <b>one</b> board into"
560 " 'bootloader' mode. Press <b>Refresh</b> when ready.</p>"
561 )
562 htmlText += "<hr/>" + SupportedUF2Boards[boardType]["instructions"]
563 self.infoEdit.setHtml(htmlText)
564
565 def __showManualInstructions(self):
566 """
567 Private method to show instructions for flashing devices manually.
568 """
569 self.infoLabel.setText(self.tr("Flash Instructions:"))
570
571 htmlText = self.tr(
572 "<h4>Flash method 'manual' selected.</h4>"
573 "<p>Follow the instructions below to flash a device by entering"
574 "the data manually.</p><ol>"
575 "<li>Change the device to 'bootloader' mode.</li>"
576 "<li>Wait until the device has entered 'bootloader' mode.</li>"
577 "<li>Ensure the boot volume is available (this may require"
578 " mounting it) and select its path.</li>"
579 "<li>Select the firmware file to be flashed and click the"
580 " flash button.</li>"
581 "</ol>"
582 )
583 for boardType in SupportedUF2Boards:
584 htmlText += "<hr/>" + SupportedUF2Boards[boardType]["instructions"]
585 self.infoEdit.setHtml(htmlText)
586
587 def __showNoVolumeInformation(self, volumes):
588 """
589 Private method to show information about the expected boot volume(s).
590
591 @param volumes list of expected volume names
592 @type list of str
593 """
594 self.infoLabel.setText(self.tr("Boot Volume not found:"))
595
596 htmlText = self.tr(
597 "<h4>No Boot Volume detected.</h4>"
598 "<p>Please ensure that the boot volume of the device to be flashed"
599 " is available. "
600 )
601 if len(volumes) == 1:
602 htmlText += self.tr(
603 "This volume should be named <b>{0}</b>."
604 " Press <b>Refresh</b> when ready.</p>"
605 ).format(volumes[0])
606 else:
607 htmlText += self.tr(
608 "This volume should have one of these names.</p>"
609 "<ul><li>{0}</li></ul>"
610 "<p>Press <b>Refresh</b> when ready.</p>"
611 ).format("</li><li>".join(sorted(volumes)))
612 self.infoEdit.setHtml(htmlText)
613
614 def __showMultipleVolumesInformation(self, volumePaths):
615 """
616 Private method to show information because multiple devices of the
617 same type are ready for flashing.
618
619 Note: This is a dangerous situation!
620
621 @param volumePaths list of volume paths
622 @type list of str
623 """
624 self.infoLabel.setText(self.tr("Multiple Boot Volumes found:"))
625
626 htmlText = self.tr(
627 "<h4>Multiple Boot Volumes were found</h4>"
628 "<p>These volume paths were found.</p><ul><li>{0}</li></ul>"
629 "<p>Please ensure that only one device of a type is ready for"
630 " flashing. Press <b>Refresh</b> when ready.</p>"
631 ).format("</li><li>".join(sorted(volumePaths)))
632 self.infoEdit.setHtml(htmlText)
633
634 @pyqtSlot()
635 def on_flashButton_clicked(self):
636 """
637 Private slot to flash the selected MicroPython or CircuitPython
638 firmware onto the device.
639 """
640 boardType = self.devicesComboBox.currentData(self.DeviceTypeRole)
641 firmwarePath = self.firmwarePicker.text()
642 volumePath = self.bootPicker.text()
643 if os.path.exists(firmwarePath) and os.path.exists(volumePath):
644 firmwareType = SupportedUF2Boards[boardType]["firmware"]
645 self.infoLabel.setText(
646 self.tr("Flashing {0}").format(firmwareType))
647 self.infoEdit.setHtml(self.tr(
648 "<p>Flashing the {0} firmware to the device. Please wait"
649 " until the device resets automatically</p>."
650 ).format(firmwareType))
651 QCoreApplication.processEvents(QEventLoop.ExcludeUserInputEvents)
652 shutil.copy2(firmwarePath, volumePath)
653 QThread.sleep(1)
654 self.on_refreshButton_clicked()
655
656 @pyqtSlot()
657 def on_refreshButton_clicked(self):
658 """
659 Private slot to refresh the dialog.
660 """
661 self.__populate()
662
663 @pyqtSlot(int)
664 def on_devicesComboBox_currentIndexChanged(self, index):
665 """
666 Private slot to handle the selection of a board.
667
668 @param index selected index
669 @type int
670 """
671 vidpid = self.devicesComboBox.itemData(index, self.DeviceVidPidRole)
672 boardType = self.devicesComboBox.itemData(index, self.DeviceTypeRole)
673
674 self.bootPicker.setEnabled(boardType == self.__manualType)
675 if boardType == self.__manualType:
676 self.__showManualInstructions()
677
678 if vidpid is None:
679 if boardType is None:
680 self.bootPicker.clear()
681 else:
682 volumes = SupportedUF2Boards[boardType]["volumes"][vidpid]
683 foundVolumes = []
684 for volume in volumes:
685 foundVolumes += Utilities.findVolume(volume, findAll=True)
686
687 if len(foundVolumes) == 0:
688 self.__showNoVolumeInformation(volumes)
689 self.bootPicker.clear()
690 elif len(foundVolumes) == 1:
691 self.bootPicker.setText(foundVolumes[0])
692 else:
693 self.__showMultipleVolumesInformation()
694 self.bootPicker.clear()
695
696 self.__updateFlashButton()
697
698 @pyqtSlot(str)
699 def on_firmwarePicker_textChanged(self, text):
700 """
701 Private slot handling a change of the firmware file.
702
703 @param text current text of the firmware edit
704 @type str
705 """
706 self.__updateFlashButton()
707
708 @pyqtSlot(str)
709 def on_bootPicker_textChanged(self, text):
710 """
711 Private slot handling a change of the boot volume.
712
713 @param text current text of the boot volume edit
714 @type str
715 """
716 self.__updateFlashButton()

eric ide

mercurial