src/eric7/MicroPython/TeensyDevices.py

branch
eric7
changeset 9755
1a09700229e7
equal deleted inserted replaced
9754:ddf0407d42be 9755:1a09700229e7
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2023 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the device interface class for Teensy boards with MicroPython.
8 """
9
10 from PyQt6.QtCore import QProcess, QUrl, pyqtSlot
11 from PyQt6.QtNetwork import QNetworkRequest
12 from PyQt6.QtWidgets import QMenu
13
14 from eric7 import Globals, Preferences
15 from eric7.EricWidgets import EricMessageBox
16 from eric7.EricWidgets.EricApplication import ericApp
17
18 from .MicroPythonDevices import FirmwareGithubUrls, MicroPythonDevice
19 from .MicroPythonWidget import HAS_QTCHART
20
21
22 class TeensyDevice(MicroPythonDevice):
23 """
24 Class implementing the device for Teensy boards with MicroPython.
25 """
26
27 def __init__(self, microPythonWidget, deviceType, parent=None):
28 """
29 Constructor
30
31 @param microPythonWidget reference to the main MicroPython widget
32 @type MicroPythonWidget
33 @param deviceType device type assigned to this device interface
34 @type str
35 @param parent reference to the parent object
36 @type QObject
37 """
38 super().__init__(microPythonWidget, deviceType, parent)
39
40 self.__createTeensyMenu()
41
42 def setButtons(self):
43 """
44 Public method to enable the supported action buttons.
45 """
46 super().setButtons()
47 self.microPython.setActionButtons(
48 run=True, repl=True, files=True, chart=HAS_QTCHART
49 )
50
51 def forceInterrupt(self):
52 """
53 Public method to determine the need for an interrupt when opening the
54 serial connection.
55
56 @return flag indicating an interrupt is needed
57 @rtype bool
58 """
59 return False
60
61 def deviceName(self):
62 """
63 Public method to get the name of the device.
64
65 @return name of the device
66 @rtype str
67 """
68 return self.tr("Teensy")
69
70 def canStartRepl(self):
71 """
72 Public method to determine, if a REPL can be started.
73
74 @return tuple containing a flag indicating it is safe to start a REPL
75 and a reason why it cannot.
76 @rtype tuple of (bool, str)
77 """
78 return True, ""
79
80 def canStartPlotter(self):
81 """
82 Public method to determine, if a Plotter can be started.
83
84 @return tuple containing a flag indicating it is safe to start a
85 Plotter and a reason why it cannot.
86 @rtype tuple of (bool, str)
87 """
88 return True, ""
89
90 def canRunScript(self):
91 """
92 Public method to determine, if a script can be executed.
93
94 @return tuple containing a flag indicating it is safe to start a
95 Plotter and a reason why it cannot.
96 @rtype tuple of (bool, str)
97 """
98 return True, ""
99
100 def runScript(self, script):
101 """
102 Public method to run the given Python script.
103
104 @param script script to be executed
105 @type str
106 """
107 pythonScript = script.split("\n")
108 self.sendCommands(pythonScript)
109
110 def canStartFileManager(self):
111 """
112 Public method to determine, if a File Manager can be started.
113
114 @return tuple containing a flag indicating it is safe to start a
115 File Manager and a reason why it cannot.
116 @rtype tuple of (bool, str)
117 """
118 return True, ""
119
120 def getDocumentationUrl(self):
121 """
122 Public method to get the device documentation URL.
123
124 @return documentation URL of the device
125 @rtype str
126 """
127 return Preferences.getMicroPython("MicroPythonDocuUrl")
128
129 def getFirmwareUrl(self):
130 """
131 Public method to get the device firmware download URL.
132
133 @return firmware download URL of the device
134 @rtype str
135 """
136 return Preferences.getMicroPython("MicroPythonFirmwareUrl")
137
138 def __createTeensyMenu(self):
139 """
140 Private method to create the microbit submenu.
141 """
142 self.__teensyMenu = QMenu(self.tr("Teensy Functions"))
143
144 self.__showMpyAct = self.__teensyMenu.addAction(
145 self.tr("Show MicroPython Versions"), self.__showFirmwareVersions
146 )
147 self.__teensyMenu.addSeparator()
148 self.__teensyMenu.addAction(
149 self.tr("MicroPython Flash Instructions"), self.__showFlashInstructions
150 )
151 self.__flashMpyAct = self.__teensyMenu.addAction(
152 self.tr("Flash MicroPython Firmware"), self.__startTeensyLoader
153 )
154 self.__flashMpyAct.setToolTip(
155 self.tr("Start the 'Teensy Loader' application to flash the Teensy device.")
156 )
157
158 def addDeviceMenuEntries(self, menu):
159 """
160 Public method to add device specific entries to the given menu.
161
162 @param menu reference to the context menu
163 @type QMenu
164 """
165 connected = self.microPython.isConnected()
166 linkConnected = self.microPython.isLinkConnected()
167
168 self.__showMpyAct.setEnabled(connected)
169 self.__flashMpyAct.setEnabled(not linkConnected)
170
171 menu.addMenu(self.__teensyMenu)
172
173 @pyqtSlot()
174 def __showFirmwareVersions(self):
175 """
176 Private slot to show the firmware version of the connected device and the
177 available firmware version.
178 """
179 if self.microPython.isConnected():
180 if self._deviceData["mpy_name"] != "micropython":
181 EricMessageBox.critical(
182 None,
183 self.tr("Show MicroPython Versions"),
184 self.tr(
185 """The firmware of the connected device cannot be"""
186 """ determined or the board does not run MicroPython."""
187 """ Aborting..."""
188 ),
189 )
190 else:
191 ui = ericApp().getObject("UserInterface")
192 request = QNetworkRequest(QUrl(FirmwareGithubUrls["micropython"]))
193 reply = ui.networkAccessManager().head(request)
194 reply.finished.connect(lambda: self.__firmwareVersionResponse(reply))
195
196 def __firmwareVersionResponse(self, reply):
197 """
198 Private method handling the response of the latest version request.
199
200 @param reply reference to the reply object
201 @type QNetworkReply
202 """
203 latestUrl = reply.url().toString()
204 tag = latestUrl.rsplit("/", 1)[-1]
205 while tag and not tag[0].isdecimal():
206 # get rid of leading non-decimal characters
207 tag = tag[1:]
208 latestVersion = Globals.versionToTuple(tag)
209
210 if self._deviceData["mpy_version"] == "unknown":
211 currentVersionStr = self.tr("unknown")
212 currentVersion = (0, 0, 0)
213 else:
214 currentVersionStr = self._deviceData["mpy_version"]
215 currentVersion = Globals.versionToTuple(currentVersionStr)
216
217 msg = self.tr(
218 "<h4>MicroPython Version Information</h4>"
219 "<table>"
220 "<tr><td>Installed:</td><td>{0}</td></tr>"
221 "<tr><td>Available:</td><td>{1}</td></tr>"
222 "</table>"
223 ).format(currentVersionStr, tag)
224 if currentVersion < latestVersion:
225 msg += self.tr("<p><b>Update available!</b></p>")
226
227 EricMessageBox.information(
228 None,
229 self.tr("MicroPython Version"),
230 msg,
231 )
232
233 def __showFlashInstructions(self):
234 """
235 Private method to show a message box with instruction to flash the Teensy.
236 """
237 EricMessageBox.information(
238 self.microPython,
239 self.tr("Flash MicroPython Firmware"),
240 self.tr(
241 """<p>Teensy 4.0 and Teensy 4.1 are flashed using the 'Teensy Loader'"""
242 """ application. Make sure you downloaded the MicroPython or"""
243 """ CircuitPython .hex file.</p>"""
244 """<p>See <a href="{0}">the PJRC Teensy web site</a>"""
245 """ for details.</p>"""
246 ).format("https://www.pjrc.com/teensy/loader.html"),
247 )
248
249 def __startTeensyLoader(self):
250 """
251 Private method to start the 'Teensy Loader' application.
252
253 Note: The application must be accessible via the application search path.
254 """
255 ok, _ = QProcess.startDetached("teensy")
256 if not ok:
257 EricMessageBox.warning(
258 self.microPython,
259 self.tr("Start 'Teensy Loader'"),
260 self.tr(
261 """<p>The 'Teensy Loader' application <b>teensy</b> could not"""
262 """ be started. Ensure it is in the application search path or"""
263 """ start it manually.</p>"""
264 ),
265 )
266
267
268 def createDevice(microPythonWidget, deviceType, vid, pid, boardName, serialNumber):
269 """
270 Function to instantiate a MicroPython device object.
271
272 @param microPythonWidget reference to the main MicroPython widget
273 @type MicroPythonWidget
274 @param deviceType device type assigned to this device interface
275 @type str
276 @param vid vendor ID
277 @type int
278 @param pid product ID
279 @type int
280 @param boardName name of the board
281 @type str
282 @param serialNumber serial number of the board
283 @type str
284 @return reference to the instantiated device object
285 @rtype PyBoardDevice
286 """
287 return TeensyDevice(microPythonWidget, deviceType)

eric ide

mercurial