src/eric7/MicroPython/Devices/MicrobitDevices.py

branch
eric7
changeset 9766
f0e22f3a5878
parent 9765
6378da868bb0
child 9799
a79430a8811d
child 9804
b7e200d35be9
equal deleted inserted replaced
9765:6378da868bb0 9766:f0e22f3a5878
13 import os 13 import os
14 import shutil 14 import shutil
15 15
16 from PyQt6.QtCore import QStandardPaths, QUrl, pyqtSlot 16 from PyQt6.QtCore import QStandardPaths, QUrl, pyqtSlot
17 from PyQt6.QtNetwork import QNetworkRequest 17 from PyQt6.QtNetwork import QNetworkRequest
18 from PyQt6.QtWidgets import QInputDialog, QLineEdit, QMenu 18 from PyQt6.QtWidgets import QMenu
19 19
20 from eric7 import Globals, Preferences 20 from eric7 import Globals, Preferences
21 from eric7.EricWidgets import EricFileDialog, EricMessageBox 21 from eric7.EricWidgets import EricFileDialog, EricMessageBox
22 from eric7.EricWidgets.EricApplication import ericApp 22 from eric7.EricWidgets.EricApplication import ericApp
23 from eric7.SystemUtilities import FileSystemUtilities 23 from eric7.SystemUtilities import FileSystemUtilities
145 The default returns True. 145 The default returns True.
146 146
147 @return flag indicating support for time commands 147 @return flag indicating support for time commands
148 @rtype bool 148 @rtype bool
149 """ 149 """
150 if ( 150 if self.microPython.isConnected() and self.hasCircuitPython():
151 self.microPython.isConnected()
152 and self.checkDeviceData()
153 and self._deviceData["mpy_name"] == "circuitpython"
154 ):
155 return True 151 return True
156 152
157 return False 153 return False
158 154
159 def __isMicroBitV1(self): 155 def __isMicroBitV1(self):
198 ) 194 )
199 self.__flashDAPLinkAct = self.__microbitMenu.addAction( 195 self.__flashDAPLinkAct = self.__microbitMenu.addAction(
200 self.tr("Flash Firmware"), lambda: self.__flashMicroPython(firmware=True) 196 self.tr("Flash Firmware"), lambda: self.__flashMicroPython(firmware=True)
201 ) 197 )
202 self.__microbitMenu.addSeparator() 198 self.__microbitMenu.addSeparator()
203 self.__saveScripAct = self.__microbitMenu.addAction(
204 self.tr("Save Script"), self.__saveScriptToDevice
205 )
206 self.__saveScripAct.setToolTip(
207 self.tr("Save the current script to the selected device")
208 )
209 self.__saveMainScriptAct = self.__microbitMenu.addAction( 199 self.__saveMainScriptAct = self.__microbitMenu.addAction(
210 self.tr("Save Script as 'main.py'"), self.__saveMain 200 self.tr("Save Script as 'main.py'"), self.__saveMain
211 ) 201 )
212 self.__saveMainScriptAct.setToolTip( 202 self.__saveMainScriptAct.setToolTip(
213 self.tr("Save the current script as 'main.py' on the connected device") 203 self.tr("Save the current script as 'main.py' on the connected device")
225 @type QMenu 215 @type QMenu
226 """ 216 """
227 connected = self.microPython.isConnected() 217 connected = self.microPython.isConnected()
228 linkConnected = self.microPython.isLinkConnected() 218 linkConnected = self.microPython.isLinkConnected()
229 219
220 aw = ericApp().getObject("ViewManager").activeWindow()
221 canSaveMain = (
222 aw is not None
223 and (aw.isPyFile() or aw.isMicroPythonFile())
224 and bool(aw.text().strip())
225 )
226
230 self.__showMpyAct.setEnabled(connected and self.getDeviceType() != "calliope") 227 self.__showMpyAct.setEnabled(connected and self.getDeviceType() != "calliope")
231 self.__flashMpyAct.setEnabled(not linkConnected) 228 self.__flashMpyAct.setEnabled(not linkConnected)
232 self.__flashDAPLinkAct.setEnabled(not linkConnected) 229 self.__flashDAPLinkAct.setEnabled(not linkConnected)
233 self.__saveScripAct.setEnabled(connected) 230 self.__saveMainScriptAct.setEnabled(connected and canSaveMain)
234 self.__saveMainScriptAct.setEnabled(connected)
235 self.__resetAct.setEnabled(connected) 231 self.__resetAct.setEnabled(connected)
236 232
237 menu.addMenu(self.__microbitMenu) 233 menu.addMenu(self.__microbitMenu)
238 234
239 def hasFlashMenuEntry(self): 235 def hasFlashMenuEntry(self):
468 def __saveMain(self): 464 def __saveMain(self):
469 """ 465 """
470 Private slot to copy the current script as 'main.py' onto the 466 Private slot to copy the current script as 'main.py' onto the
471 connected device. 467 connected device.
472 """ 468 """
473 self.__saveScriptToDevice("main.py")
474
475 @pyqtSlot()
476 def __saveScriptToDevice(self, scriptName=""):
477 """
478 Private method to save the current script onto the connected
479 device.
480
481 @param scriptName name of the file on the device
482 @type str
483 """
484 aw = ericApp().getObject("ViewManager").activeWindow() 469 aw = ericApp().getObject("ViewManager").activeWindow()
485 if not aw: 470 if not aw:
486 return 471 return
487 472
488 title = ( 473 title = self.tr("Save Script as 'main.py'")
489 self.tr("Save Script as '{0}'").format(scriptName)
490 if scriptName
491 else self.tr("Save Script")
492 )
493 474
494 if not (aw.isPyFile() or aw.isMicroPythonFile()): 475 if not (aw.isPyFile() or aw.isMicroPythonFile()):
495 yes = EricMessageBox.yesNo( 476 yes = EricMessageBox.yesNo(
496 self.microPython, 477 self.microPython,
497 title, 478 title,
508 EricMessageBox.warning( 489 EricMessageBox.warning(
509 self.microPython, title, self.tr("""The script is empty. Aborting.""") 490 self.microPython, title, self.tr("""The script is empty. Aborting.""")
510 ) 491 )
511 return 492 return
512 493
513 if not scriptName: 494 self.putData("main.py", script.encode("utf-8"))
514 scriptName = os.path.basename(aw.getFileName())
515 scriptName, ok = QInputDialog.getText(
516 self.microPython,
517 title,
518 self.tr("Enter a file name on the device:"),
519 QLineEdit.EchoMode.Normal,
520 scriptName,
521 )
522 if not ok or not bool(scriptName):
523 return
524
525 title = self.tr("Save Script as '{0}'").format(scriptName)
526
527 commands = [
528 "fd = open('{0}', 'wb')".format(scriptName),
529 "f = fd.write",
530 ]
531 for line in script.splitlines():
532 commands.append("f(" + repr(line + "\n") + ")")
533 commands.append("fd.close()")
534 out, err = self.microPython.deviceInterface().execute(commands)
535 if err:
536 EricMessageBox.critical(
537 self.microPython,
538 title,
539 self.tr(
540 """<p>The script could not be saved to the"""
541 """ device.</p><p>Reason: {0}</p>"""
542 ).format(err.decode("utf-8")),
543 )
544 495
545 # reset the device 496 # reset the device
546 self.__resetDevice() 497 self.__resetDevice()
547 498
548 @pyqtSlot() 499 @pyqtSlot()
551 Private slot to reset the connected device. 502 Private slot to reset the connected device.
552 """ 503 """
553 if self.getDeviceType() == "bbc_microbit": 504 if self.getDeviceType() == "bbc_microbit":
554 # BBC micro:bit 505 # BBC micro:bit
555 self.microPython.deviceInterface().execute( 506 self.microPython.deviceInterface().execute(
556 [ 507 "import microbit\nmicrobit.reset()\n"
557 "import microbit",
558 "microbit.reset()",
559 ]
560 ) 508 )
561 else: 509 else:
562 # Calliope mini 510 # Calliope mini
563 self.microPython.deviceInterface().execute( 511 self.microPython.deviceInterface().execute(
564 [ 512 "import calliope_mini\ncalliope_mini.reset()\n"
565 "import calliope_mini",
566 "calliope_mini.reset()",
567 ]
568 ) 513 )
569 514
570 def getDocumentationUrl(self): 515 def getDocumentationUrl(self):
571 """ 516 """
572 Public method to get the device documentation URL. 517 Public method to get the device documentation URL.
645 @type str 590 @type str
646 @return tuple containg the directory listing 591 @return tuple containg the directory listing
647 @rtype tuple of str 592 @rtype tuple of str
648 @exception OSError raised to indicate an issue with the device 593 @exception OSError raised to indicate an issue with the device
649 """ 594 """
650 # BBC micro:bit does not support directories 595 if self.hasCircuitPython():
651 commands = [ 596 return super().ls(dirname=dirname)
652 "import os as __os_", 597 else:
653 "print(__os_.listdir())", 598 # BBC micro:bit with MicroPython does not support directories
654 "del __os_", 599 command = """
655 ] 600 import os as __os_
656 out, err = self._interface.execute(commands) 601 print(__os_.listdir())
657 if err: 602 del __os_
658 raise OSError(self._shortError(err)) 603 """
659 return ast.literal_eval(out.decode("utf-8")) 604 out, err = self._interface.execute(command)
605 if err:
606 raise OSError(self._shortError(err))
607 return ast.literal_eval(out.decode("utf-8"))
660 608
661 def lls(self, dirname="", fullstat=False, showHidden=False): 609 def lls(self, dirname="", fullstat=False, showHidden=False):
662 """ 610 """
663 Public method to get a long directory listing of the connected device 611 Public method to get a long directory listing of the connected device
664 including meta data. 612 including meta data.
674 false) or the complete stat() tuple. 'None' is returned in case the 622 false) or the complete stat() tuple. 'None' is returned in case the
675 directory doesn't exist. 623 directory doesn't exist.
676 @rtype tuple of (str, tuple) 624 @rtype tuple of (str, tuple)
677 @exception OSError raised to indicate an issue with the device 625 @exception OSError raised to indicate an issue with the device
678 """ 626 """
679 # BBC micro:bit does not support directories 627 if self.hasCircuitPython():
680 commands = [ 628 return super().lls(
681 "import os as __os_", 629 dirname=dirname, fullstat=fullstat, showHidden=showHidden
682 "\n".join( 630 )
683 [ 631 else:
684 "def is_visible(filename, showHidden):", 632 # BBC micro:bit with MicroPython does not support directories
685 " return showHidden or " 633 command = """
686 "(filename[0] != '.' and filename[-1] != '~')", 634 import os as __os_
687 ] 635
688 ), 636 def is_visible(filename, showHidden):
689 "\n".join( 637 return showHidden or (filename[0] != '.' and filename[-1] != '~')
690 [ 638
691 "def stat(filename):", 639 def stat(filename):
692 " size = __os_.size(filename)", 640 size = __os_.size(filename)
693 " return (0, 0, 0, 0, 0, 0, size, 0, 0, 0)", 641 return (0, 0, 0, 0, 0, 0, size, 0, 0, 0)
694 ] 642
695 ), 643 def listdir_stat(showHidden):
696 "\n".join( 644 files = __os_.listdir()
697 [ 645 return list((f, stat(f)) for f in files if is_visible(f,showHidden))
698 "def listdir_stat(showHidden):", 646
699 " files = __os_.listdir()", 647 print(listdir_stat({0}))
700 " return list((f, stat(f)) for f in files if" 648 del __os_, stat, listdir_stat, is_visible
701 " is_visible(f,showHidden))", 649 """.format(
702 ] 650 showHidden
703 ), 651 )
704 "print(listdir_stat({0}))".format(showHidden), 652 out, err = self._interface.execute(command)
705 "del __os_, stat, listdir_stat, is_visible", 653 if err:
706 ] 654 raise OSError(self._shortError(err))
707 out, err = self._interface.execute(commands) 655 fileslist = ast.literal_eval(out.decode("utf-8"))
708 if err: 656 if fileslist is None:
709 raise OSError(self._shortError(err)) 657 return None
710 fileslist = ast.literal_eval(out.decode("utf-8"))
711 if fileslist is None:
712 return None
713 else:
714 if fullstat:
715 return fileslist
716 else: 658 else:
717 return [(f, (s[0], s[6], s[8])) for f, s in fileslist] 659 if fullstat:
660 return fileslist
661 else:
662 return [(f, (s[0], s[6], s[8])) for f, s in fileslist]
718 663
719 def pwd(self): 664 def pwd(self):
720 """ 665 """
721 Public method to get the current directory of the connected device. 666 Public method to get the current directory of the connected device.
722 667
723 @return current directory 668 @return current directory
724 @rtype str 669 @rtype str
725 """ 670 """
726 # BBC micro:bit does not support directories 671 if self.hasCircuitPython():
727 return "" 672 return super().pwd()
673 else:
674 # BBC micro:bit with MicroPython does not support directories
675 return ""
728 676
729 ################################################################## 677 ##################################################################
730 ## time related methods below 678 ## time related methods below
731 ################################################################## 679 ##################################################################
732 680
747 # rtc_time[4] - hour 0..23 695 # rtc_time[4] - hour 0..23
748 # rtc_time[5] - minute 0..59 696 # rtc_time[5] - minute 0..59
749 # rtc_time[6] - second 0..59 697 # rtc_time[6] - second 0..59
750 # rtc_time[7] - yearday 1..366 698 # rtc_time[7] - yearday 1..366
751 # rtc_time[8] - isdst 0, 1, or -1 699 # rtc_time[8] - isdst 0, 1, or -1
752 return "" 700 if self.hasCircuitPython():
701 return super()._getSetTimeCode()
702 else:
703 return ""
753 704
754 705
755 def createDevice(microPythonWidget, deviceType, vid, pid, boardName, serialNumber): 706 def createDevice(microPythonWidget, deviceType, vid, pid, boardName, serialNumber):
756 """ 707 """
757 Function to instantiate a MicroPython device object. 708 Function to instantiate a MicroPython device object.

eric ide

mercurial