9 """ |
9 """ |
10 |
10 |
11 import os |
11 import os |
12 import shutil |
12 import shutil |
13 |
13 |
14 from PyQt6.QtCore import QStandardPaths, pyqtSlot |
14 from PyQt6.QtCore import QStandardPaths, QUrl, pyqtSlot |
|
15 from PyQt6.QtNetwork import QNetworkRequest |
15 from PyQt6.QtWidgets import QInputDialog, QLineEdit |
16 from PyQt6.QtWidgets import QInputDialog, QLineEdit |
16 |
17 |
17 from eric7 import Preferences |
18 from eric7 import Globals, Preferences |
18 from eric7.EricWidgets import EricFileDialog, EricMessageBox |
19 from eric7.EricWidgets import EricFileDialog, EricMessageBox |
19 from eric7.EricWidgets.EricApplication import ericApp |
20 from eric7.EricWidgets.EricApplication import ericApp |
20 from eric7.SystemUtilities import FileSystemUtilities |
21 from eric7.SystemUtilities import FileSystemUtilities |
21 |
22 |
22 from .MicroPythonDevices import MicroPythonDevice |
23 from .MicroPythonDevices import FirmwareGithubUrls, MicroPythonDevice |
23 from .MicroPythonWidget import HAS_QTCHART |
24 from .MicroPythonWidget import HAS_QTCHART |
24 |
25 |
25 |
26 |
26 class MicrobitDevice(MicroPythonDevice): |
27 class MicrobitDevice(MicroPythonDevice): |
27 """ |
28 """ |
141 |
142 |
142 @param menu reference to the context menu |
143 @param menu reference to the context menu |
143 @type QMenu |
144 @type QMenu |
144 """ |
145 """ |
145 connected = self.microPython.isConnected() |
146 connected = self.microPython.isConnected() |
146 |
147 linkConnected = self.microPython.isLinkConnected() |
147 act = menu.addAction(self.tr("Flash MicroPython"), self.__flashMicroPython) |
148 |
148 act.setEnabled(not connected) |
149 menu.addAction( |
149 act = menu.addAction( |
150 self.tr("Show MicroPython Versions"), self.__showFirmwareVersions |
|
151 ).setEnabled(connected and self.getDeviceType() != "calliope") |
|
152 menu.addAction( |
|
153 self.tr("Flash MicroPython"), self.__flashMicroPython |
|
154 ).setEnabled(not linkConnected) |
|
155 menu.addAction( |
150 self.tr("Flash Firmware"), lambda: self.__flashMicroPython(firmware=True) |
156 self.tr("Flash Firmware"), lambda: self.__flashMicroPython(firmware=True) |
151 ) |
157 ).setEnabled(not linkConnected) |
152 act.setEnabled(not connected) |
|
153 menu.addSeparator() |
158 menu.addSeparator() |
154 act = menu.addAction(self.tr("Save Script"), self.__saveScriptToDevice) |
159 act = menu.addAction(self.tr("Save Script"), self.__saveScriptToDevice) |
155 act.setToolTip(self.tr("Save the current script to the selected device")) |
160 act.setToolTip(self.tr("Save the current script to the selected device")) |
156 act.setEnabled(connected) |
161 act.setEnabled(connected) |
157 act = menu.addAction(self.tr("Save Script as 'main.py'"), self.__saveMain) |
162 act = menu.addAction(self.tr("Save Script as 'main.py'"), self.__saveMain) |
158 act.setToolTip( |
163 act.setToolTip( |
159 self.tr("Save the current script as 'main.py' on the connected device") |
164 self.tr("Save the current script as 'main.py' on the connected device") |
160 ) |
165 ) |
161 act.setEnabled(connected) |
166 act.setEnabled(connected) |
162 menu.addSeparator() |
167 menu.addSeparator() |
163 act = menu.addAction( |
168 menu.addAction( |
164 self.tr("Reset {0}").format(self.deviceName()), self.__resetDevice |
169 self.tr("Reset {0}").format(self.deviceName()), self.__resetDevice |
165 ) |
170 ).setEnabled(connected) |
166 act.setEnabled(connected) |
|
167 |
171 |
168 def hasFlashMenuEntry(self): |
172 def hasFlashMenuEntry(self): |
169 """ |
173 """ |
170 Public method to check, if the device has its own flash menu entry. |
174 Public method to check, if the device has its own flash menu entry. |
171 |
175 |
289 " Please make sure, that only one device is prepared." |
293 " Please make sure, that only one device is prepared." |
290 ), |
294 ), |
291 ) |
295 ) |
292 |
296 |
293 @pyqtSlot() |
297 @pyqtSlot() |
|
298 def __showFirmwareVersions(self): |
|
299 """ |
|
300 Private slot to show the firmware version of the connected device and the |
|
301 available firmware version. |
|
302 """ |
|
303 if self.microPython.isConnected(): |
|
304 interface = self.microPython.commandsInterface() |
|
305 if interface is not None: |
|
306 impInfo = interface.getImplementation() |
|
307 versionInfo = interface.version() |
|
308 if impInfo["name"] != "micropython": |
|
309 EricMessageBox.critical( |
|
310 None, |
|
311 self.tr("Show MicroPython Versions"), |
|
312 self.tr( |
|
313 """The firmware of the connected device cannot be""" |
|
314 """ determined or the board does not run MicroPython.""" |
|
315 """ Aborting...""" |
|
316 ), |
|
317 ) |
|
318 else: |
|
319 impInfo["version"] = versionInfo["release"] |
|
320 if self.getDeviceType() == "bbc_microbit": |
|
321 if "nRF51822" in versionInfo["machine"]: |
|
322 url = QUrl(FirmwareGithubUrls["microbit_v1"]) |
|
323 elif "nRF52833" in versionInfo["machine"]: |
|
324 url = QUrl(FirmwareGithubUrls["microbit_v2"]) |
|
325 else: |
|
326 EricMessageBox.critical( |
|
327 None, |
|
328 self.tr("Show MicroPython Versions"), |
|
329 self.tr( |
|
330 """<p>The BBC micro:bit generation cannot be""" |
|
331 """ determined. Aborting...</p>""" |
|
332 ), |
|
333 ) |
|
334 return |
|
335 else: |
|
336 EricMessageBox.critical( |
|
337 None, |
|
338 self.tr("Show MicroPython Versions"), |
|
339 self.tr( |
|
340 """<p>The firmware URL for the device type <b>{0}</b>""" |
|
341 """ is not known. Aborting...</p>""" |
|
342 ).format(self.getDeviceType()), |
|
343 ) |
|
344 return |
|
345 |
|
346 ui = ericApp().getObject("UserInterface") |
|
347 request = QNetworkRequest(url) |
|
348 reply = ui.networkAccessManager().head(request) |
|
349 reply.finished.connect( |
|
350 lambda: self.__firmwareVersionResponse(reply, impInfo) |
|
351 ) |
|
352 |
|
353 def __firmwareVersionResponse(self, reply, implementation): |
|
354 """ |
|
355 Private method handling the response of the latest version request. |
|
356 |
|
357 @param reply reference to the reply object |
|
358 @type QNetworkReply |
|
359 @param implementation dictionary containing the implementation data of the |
|
360 connected device |
|
361 @type dict |
|
362 """ |
|
363 latestUrl = reply.url().toString() |
|
364 tag = latestUrl.rsplit("/", 1)[-1] |
|
365 while tag and not tag[0].isdecimal(): |
|
366 # get rid of leading non-decimal characters |
|
367 tag = tag[1:] |
|
368 latestVersion = Globals.versionToTuple(tag) |
|
369 |
|
370 if implementation["version"] == "unknown": |
|
371 currentVersionStr = self.tr("unknown") |
|
372 currentVersion = (0, 0, 0) |
|
373 else: |
|
374 currentVersionStr = implementation["version"] |
|
375 currentVersion = Globals.versionToTuple(currentVersionStr) |
|
376 |
|
377 msg = self.tr( |
|
378 "<h4>MicroPython Version Information<br/>" |
|
379 "(BBC micro:bit v{2})</h4>" |
|
380 "<table>" |
|
381 "<tr><td>Installed:</td><td>{0}</td></tr>" |
|
382 "<tr><td>Available:</td><td>{1}</td></tr>" |
|
383 "</table>" |
|
384 ).format(currentVersionStr, tag, currentVersionStr[0]) |
|
385 if currentVersion < latestVersion: |
|
386 msg += self.tr("<p><b>Update available!</b></p>") |
|
387 |
|
388 EricMessageBox.information( |
|
389 None, |
|
390 self.tr("MicroPython Version"), |
|
391 msg, |
|
392 ) |
|
393 |
|
394 @pyqtSlot() |
294 def __saveMain(self): |
395 def __saveMain(self): |
295 """ |
396 """ |
296 Private slot to copy the current script as 'main.py' onto the |
397 Private slot to copy the current script as 'main.py' onto the |
297 connected device. |
398 connected device. |
298 """ |
399 """ |