eric6/MicroPython/MicrobitDevices.py

changeset 7299
a22b87b46128
parent 7229
53054eb5b15a
child 7328
e2d85ef3fadb
equal deleted inserted replaced
7298:9d15757d06ac 7299:a22b87b46128
6 """ 6 """
7 Module implementing the device interface class for BBC micro:bit boards. 7 Module implementing the device interface class for BBC micro:bit boards.
8 """ 8 """
9 9
10 10
11 import sys
12 import os 11 import os
12 import shutil
13 13
14 from PyQt5.QtCore import pyqtSlot, QStandardPaths 14 from PyQt5.QtCore import pyqtSlot, QStandardPaths
15 from PyQt5.QtWidgets import QInputDialog, QLineEdit
15 16
16 from .MicroPythonDevices import MicroPythonDevice 17 from .MicroPythonDevices import MicroPythonDevice
17 from .MicroPythonWidget import HAS_QTCHART 18 from .MicroPythonWidget import HAS_QTCHART
18 19
19 from E5Gui import E5MessageBox, E5FileDialog 20 from E5Gui import E5MessageBox, E5FileDialog
20 from E5Gui.E5Application import e5App 21 from E5Gui.E5Application import e5App
21 from E5Gui.E5ProcessDialog import E5ProcessDialog
22 22
23 import Utilities 23 import Utilities
24 import Preferences 24 import Preferences
25 25
26 26
158 @param menu reference to the context menu 158 @param menu reference to the context menu
159 @type QMenu 159 @type QMenu
160 """ 160 """
161 connected = self.microPython.isConnected() 161 connected = self.microPython.isConnected()
162 162
163 act = menu.addAction(self.tr("Flash Default MicroPython Firmware"), 163 act = menu.addAction(self.tr("Flash MicroPython Firmware"),
164 self.__flashMicroPython) 164 self.__flashMicroPython)
165 act.setEnabled(not connected) 165 act.setEnabled(not connected)
166 act = menu.addAction(self.tr("Flash Custom MicroPython Firmware"),
167 self.__flashCustomMicroPython)
168 act.setEnabled(not connected)
169 menu.addSeparator() 166 menu.addSeparator()
170 act = menu.addAction(self.tr("Flash Script"), self.__flashScript) 167 act = menu.addAction(self.tr("Save Script"), self.__saveScriptToDevice)
171 act.setToolTip(self.tr( 168 act.setToolTip(self.tr(
172 "Flash the current script to the selected device.")) 169 "Save the current script to the selected device"))
173 act.setEnabled(not connected) 170 act.setEnabled(connected)
174 act = menu.addAction(self.tr("Save Script as 'main.py'"), 171 act = menu.addAction(self.tr("Save Script as 'main.py'"),
175 self.__saveMain) 172 self.__saveMain)
176 act.setToolTip(self.tr( 173 act.setToolTip(self.tr(
177 "Save the current script as 'main.py' on the connected device")) 174 "Save the current script as 'main.py' on the connected device"))
178 act.setEnabled(connected) 175 act.setEnabled(connected)
179 menu.addSeparator() 176 menu.addSeparator()
180 act = menu.addAction(self.tr("Reset micro:bit"), self.__resetDevice) 177 act = menu.addAction(self.tr("Reset micro:bit"), self.__resetDevice)
181 act.setEnabled(connected) 178 act.setEnabled(connected)
182 menu.addSeparator()
183 menu.addAction(self.tr("Install 'uflash'"), self.__installUflashTool)
184 179
185 @pyqtSlot() 180 @pyqtSlot()
186 def __flashMicroPython(self): 181 def __flashMicroPython(self):
187 """ 182 """
188 Private slot to flash the default MicroPython firmware to the device. 183 Private slot to flash the default MicroPython firmware to the device.
189 """ 184 """
190 flashArgs = [ 185 # Attempts to find the path on the filesystem that represents the
191 "-u", 186 # plugged in micro:bit board in maintenance mode.
192 "-m", "uflash", 187 deviceDirectory = Utilities.findVolume("MAINTENANCE")
193 ] 188 if not deviceDirectory:
194 dlg = E5ProcessDialog(self.tr("'uflash' Output"), 189 # BBC micro:bit is not ready or not mounted
195 self.tr("Flash Default MicroPython Firmware")) 190 E5MessageBox.critical(
196 res = dlg.startProcess(sys.executable, flashArgs) 191 self.microPython,
197 if res: 192 self.tr("Flash MicroPython Firmware"),
198 dlg.exec_() 193 self.tr(
194 'The BBC micro:bit is not ready for flashing. See the'
195 ' <a href="https://microbit.org/guide/firmware/">'
196 'micro:bit web site</a> for details.'
197 ))
198 else:
199 downloadsPath = QStandardPaths.standardLocations(
200 QStandardPaths.DownloadLocation)[0]
201 firmware = E5FileDialog.getOpenFileName(
202 self.microPython,
203 self.tr("Flash MicroPython Firmware"),
204 downloadsPath,
205 self.tr("MicroPython Firmware Files (*.hex);;All Files (*)"))
206 if firmware and os.path.exists(firmware):
207 shutil.copy2(firmware, deviceDirectory)
199 208
200 @pyqtSlot() 209 @pyqtSlot()
201 def __flashCustomMicroPython(self): 210 def __saveMain(self):
202 """ 211 """
203 Private slot to flash a custom MicroPython firmware to the device. 212 Private slot to copy the current script as 'main.py' onto the
204 """ 213 connected device.
205 downloadsPath = QStandardPaths.standardLocations( 214 """
206 QStandardPaths.DownloadLocation)[0] 215 self.__saveScriptToDevice("main.py")
207 firmware = E5FileDialog.getOpenFileName(
208 self.microPython,
209 self.tr("Flash Custom MicroPython Firmware"),
210 downloadsPath,
211 self.tr("MicroPython Firmware Files (*.hex);;All Files (*)"))
212 if firmware and os.path.exists(firmware):
213 flashArgs = [
214 "-u",
215 "-m", "uflash",
216 "--runtime", firmware,
217 ]
218 dlg = E5ProcessDialog(
219 self.tr("'uflash' Output"),
220 self.tr("Flash Default MicroPython Firmware"))
221 res = dlg.startProcess(sys.executable, flashArgs)
222 if res:
223 dlg.exec_()
224 216
225 @pyqtSlot() 217 @pyqtSlot()
226 def __flashScript(self): 218 def __saveScriptToDevice(self, scriptName=""):
227 """ 219 """
228 Private slot to flash the current script onto the selected device. 220 Private method to save the current script onto the connected
221 device.
222
223 @param scriptName name of the file on the device
224 @type str
229 """ 225 """
230 aw = e5App().getObject("ViewManager").activeWindow() 226 aw = e5App().getObject("ViewManager").activeWindow()
231 if not aw: 227 if not aw:
232 return 228 return
233 229
230 if scriptName:
231 title = self.tr("Save Script as '{0}'").format(scriptName)
232 else:
233 title = self.tr("Save Script")
234
234 if not (aw.isPyFile() or aw.isMicroPythonFile()): 235 if not (aw.isPyFile() or aw.isMicroPythonFile()):
235 yes = E5MessageBox.yesNo( 236 yes = E5MessageBox.yesNo(
236 self.microPython, 237 self.microPython,
237 self.tr("Flash Script"), 238 title,
238 self.tr("""The current editor does not contain a Python"""
239 """ script. Flash it anyway?"""))
240 if not yes:
241 return
242
243 script = aw.text().strip()
244 if not script:
245 E5MessageBox.warning(
246 self.microPython,
247 self.tr("Flash Script"),
248 self.tr("""The script is empty. Aborting."""))
249 return
250
251 if aw.checkDirty():
252 filename = aw.getFileName()
253 flashArgs = [
254 "-u",
255 "-m", "uflash",
256 filename,
257 ]
258 dlg = E5ProcessDialog(self.tr("'uflash' Output"),
259 self.tr("Flash Script"))
260 res = dlg.startProcess(sys.executable, flashArgs)
261 if res:
262 dlg.exec_()
263
264 @pyqtSlot()
265 def __saveMain(self):
266 """
267 Private slot to copy the current script as 'main.py' onto the
268 connected device.
269 """
270 aw = e5App().getObject("ViewManager").activeWindow()
271 if not aw:
272 return
273
274 if not (aw.isPyFile() or aw.isMicroPythonFile()):
275 yes = E5MessageBox.yesNo(
276 self.microPython,
277 self.tr("Save Script as 'main.py'"),
278 self.tr("""The current editor does not contain a Python""" 239 self.tr("""The current editor does not contain a Python"""
279 """ script. Write it anyway?""")) 240 """ script. Write it anyway?"""))
280 if not yes: 241 if not yes:
281 return 242 return
282 243
283 script = aw.text().strip() 244 script = aw.text().strip()
284 if not script: 245 if not script:
285 E5MessageBox.warning( 246 E5MessageBox.warning(
286 self.microPython, 247 self.microPython,
287 self.tr("Save Script as 'main.py'"), 248 title,
288 self.tr("""The script is empty. Aborting.""")) 249 self.tr("""The script is empty. Aborting."""))
289 return 250 return
290 251
252 if not scriptName:
253 scriptName = os.path.basename(aw.getFileName())
254 scriptName, ok = QInputDialog.getText(
255 self.microPython,
256 title,
257 self.tr("Enter a file name on the device:"),
258 QLineEdit.Normal,
259 scriptName)
260 if not ok or not bool(scriptName):
261 return
262
263 title = self.tr("Save Script as '{0}'").format(scriptName)
264
291 commands = [ 265 commands = [
292 "fd = open('main.py', 'wb')", 266 "fd = open('{0}', 'wb')".format(scriptName),
293 "f = fd.write", 267 "f = fd.write",
294 ] 268 ]
295 for line in script.splitlines(): 269 for line in script.splitlines():
296 commands.append("f(" + repr(line + "\n") + ")") 270 commands.append("f(" + repr(line + "\n") + ")")
297 commands.append("fd.close()") 271 commands.append("fd.close()")
298 out, err = self.microPython.commandsInterface().execute(commands) 272 out, err = self.microPython.commandsInterface().execute(commands)
299 if err: 273 if err:
300 E5MessageBox.critical( 274 E5MessageBox.critical(
301 self.microPython, 275 self.microPython,
302 self.tr("Save Script as 'main.py'"), 276 title,
303 self.tr("""<p>The script could not be saved to the""" 277 self.tr("""<p>The script could not be saved to the"""
304 """ device.</p><p>Reason: {0}</p>""") 278 """ device.</p><p>Reason: {0}</p>""")
305 .format(err.decode("utf-8"))) 279 .format(err.decode("utf-8")))
306 280
307 # reset the device 281 # reset the device
282 self.__resetDevice()
283
284 @pyqtSlot()
285 def __resetDevice(self):
286 """
287 Private slot to reset the connected device.
288 """
308 self.microPython.commandsInterface().execute([ 289 self.microPython.commandsInterface().execute([
309 "import microbit", 290 "import microbit",
310 "microbit.reset()", 291 "microbit.reset()",
311 ]) 292 ])
312 293
313 @pyqtSlot()
314 def __resetDevice(self):
315 """
316 Private slot to reset the connected device.
317 """
318 self.microPython.commandsInterface().execute([
319 "import microbit",
320 "microbit.reset()",
321 ])
322
323 @pyqtSlot()
324 def __installUflashTool(self):
325 """
326 Private slot to install the uflash package via pip.
327 """
328 pip = e5App().getObject("Pip")
329 pip.installPackages(["uflash"], interpreter=sys.executable)
330
331 def getDocumentationUrl(self): 294 def getDocumentationUrl(self):
332 """ 295 """
333 Public method to get the device documentation URL. 296 Public method to get the device documentation URL.
334 297
335 @return documentation URL of the device 298 @return documentation URL of the device

eric ide

mercurial