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 |