180 |
191 |
181 |
192 |
182 class MicroPythonWidget(QWidget, Ui_MicroPythonWidget): |
193 class MicroPythonWidget(QWidget, Ui_MicroPythonWidget): |
183 """ |
194 """ |
184 Class implementing the MicroPython REPL widget. |
195 Class implementing the MicroPython REPL widget. |
185 |
196 |
186 @signal dataReceived(data) emitted to send data received via the serial |
197 @signal dataReceived(data) emitted to send data received via the serial |
187 connection for further processing |
198 connection for further processing |
188 """ |
199 """ |
|
200 |
189 ZoomMin = -10 |
201 ZoomMin = -10 |
190 ZoomMax = 20 |
202 ZoomMax = 20 |
191 |
203 |
192 DeviceTypeRole = Qt.ItemDataRole.UserRole |
204 DeviceTypeRole = Qt.ItemDataRole.UserRole |
193 DeviceBoardRole = Qt.ItemDataRole.UserRole + 1 |
205 DeviceBoardRole = Qt.ItemDataRole.UserRole + 1 |
194 DevicePortRole = Qt.ItemDataRole.UserRole + 2 |
206 DevicePortRole = Qt.ItemDataRole.UserRole + 2 |
195 DeviceVidRole = Qt.ItemDataRole.UserRole + 3 |
207 DeviceVidRole = Qt.ItemDataRole.UserRole + 3 |
196 DevicePidRole = Qt.ItemDataRole.UserRole + 4 |
208 DevicePidRole = Qt.ItemDataRole.UserRole + 4 |
197 |
209 |
198 dataReceived = pyqtSignal(bytes) |
210 dataReceived = pyqtSignal(bytes) |
199 |
211 |
200 ManualMarker = "<manual>" |
212 ManualMarker = "<manual>" |
201 |
213 |
202 def __init__(self, parent=None): |
214 def __init__(self, parent=None): |
203 """ |
215 """ |
204 Constructor |
216 Constructor |
205 |
217 |
206 @param parent reference to the parent widget |
218 @param parent reference to the parent widget |
207 @type QWidget |
219 @type QWidget |
208 """ |
220 """ |
209 super().__init__(parent) |
221 super().__init__(parent) |
210 self.setupUi(self) |
222 self.setupUi(self) |
211 |
223 |
212 self.layout().setContentsMargins(0, 3, 0, 0) |
224 self.layout().setContentsMargins(0, 3, 0, 0) |
213 |
225 |
214 self.__ui = parent |
226 self.__ui = parent |
215 |
227 |
216 self.__superMenu = QMenu(self) |
228 self.__superMenu = QMenu(self) |
217 self.__superMenu.aboutToShow.connect(self.__aboutToShowSuperMenu) |
229 self.__superMenu.aboutToShow.connect(self.__aboutToShowSuperMenu) |
218 |
230 |
219 self.menuButton.setObjectName( |
231 self.menuButton.setObjectName("micropython_supermenu_button") |
220 "micropython_supermenu_button") |
|
221 self.menuButton.setIcon(UI.PixmapCache.getIcon("superMenu")) |
232 self.menuButton.setIcon(UI.PixmapCache.getIcon("superMenu")) |
222 self.menuButton.setToolTip(self.tr("MicroPython Menu")) |
233 self.menuButton.setToolTip(self.tr("MicroPython Menu")) |
223 self.menuButton.setPopupMode( |
234 self.menuButton.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup) |
224 QToolButton.ToolButtonPopupMode.InstantPopup) |
235 self.menuButton.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly) |
225 self.menuButton.setToolButtonStyle( |
|
226 Qt.ToolButtonStyle.ToolButtonIconOnly) |
|
227 self.menuButton.setFocusPolicy(Qt.FocusPolicy.NoFocus) |
236 self.menuButton.setFocusPolicy(Qt.FocusPolicy.NoFocus) |
228 self.menuButton.setAutoRaise(True) |
237 self.menuButton.setAutoRaise(True) |
229 self.menuButton.setShowMenuInside(True) |
238 self.menuButton.setShowMenuInside(True) |
230 self.menuButton.setMenu(self.__superMenu) |
239 self.menuButton.setMenu(self.__superMenu) |
231 |
240 |
232 self.deviceIconLabel.setPixmap(MicroPythonDevices.getDeviceIcon( |
241 self.deviceIconLabel.setPixmap(MicroPythonDevices.getDeviceIcon("", False)) |
233 "", False)) |
242 |
234 |
|
235 self.openButton.setIcon(UI.PixmapCache.getIcon("open")) |
243 self.openButton.setIcon(UI.PixmapCache.getIcon("open")) |
236 self.saveButton.setIcon(UI.PixmapCache.getIcon("fileSaveAs")) |
244 self.saveButton.setIcon(UI.PixmapCache.getIcon("fileSaveAs")) |
237 |
245 |
238 self.checkButton.setIcon(UI.PixmapCache.getIcon("question")) |
246 self.checkButton.setIcon(UI.PixmapCache.getIcon("question")) |
239 self.runButton.setIcon(UI.PixmapCache.getIcon("start")) |
247 self.runButton.setIcon(UI.PixmapCache.getIcon("start")) |
240 self.replButton.setIcon(UI.PixmapCache.getIcon("terminal")) |
248 self.replButton.setIcon(UI.PixmapCache.getIcon("terminal")) |
241 self.filesButton.setIcon(UI.PixmapCache.getIcon("filemanager")) |
249 self.filesButton.setIcon(UI.PixmapCache.getIcon("filemanager")) |
242 self.chartButton.setIcon(UI.PixmapCache.getIcon("chart")) |
250 self.chartButton.setIcon(UI.PixmapCache.getIcon("chart")) |
243 self.connectButton.setIcon(UI.PixmapCache.getIcon("linkConnect")) |
251 self.connectButton.setIcon(UI.PixmapCache.getIcon("linkConnect")) |
244 |
252 |
245 self.__zoomLayout = QHBoxLayout() |
253 self.__zoomLayout = QHBoxLayout() |
246 spacerItem = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, |
254 spacerItem = QSpacerItem( |
247 QSizePolicy.Policy.Minimum) |
255 40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum |
|
256 ) |
248 self.__zoomLayout.addSpacerItem(spacerItem) |
257 self.__zoomLayout.addSpacerItem(spacerItem) |
249 |
258 |
250 self.__zoom0 = self.replEdit.fontPointSize() |
259 self.__zoom0 = self.replEdit.fontPointSize() |
251 self.__zoomWidget = EricZoomWidget( |
260 self.__zoomWidget = EricZoomWidget( |
252 UI.PixmapCache.getPixmap("zoomOut"), |
261 UI.PixmapCache.getPixmap("zoomOut"), |
253 UI.PixmapCache.getPixmap("zoomIn"), |
262 UI.PixmapCache.getPixmap("zoomIn"), |
254 UI.PixmapCache.getPixmap("zoomReset"), self) |
263 UI.PixmapCache.getPixmap("zoomReset"), |
|
264 self, |
|
265 ) |
255 self.__zoomLayout.addWidget(self.__zoomWidget) |
266 self.__zoomLayout.addWidget(self.__zoomWidget) |
256 self.layout().insertLayout( |
267 self.layout().insertLayout(self.layout().count() - 1, self.__zoomLayout) |
257 self.layout().count() - 1, |
|
258 self.__zoomLayout) |
|
259 self.__zoomWidget.setMinimum(self.ZoomMin) |
268 self.__zoomWidget.setMinimum(self.ZoomMin) |
260 self.__zoomWidget.setMaximum(self.ZoomMax) |
269 self.__zoomWidget.setMaximum(self.ZoomMax) |
261 self.__zoomWidget.valueChanged.connect(self.__doZoom) |
270 self.__zoomWidget.valueChanged.connect(self.__doZoom) |
262 self.__currentZoom = 0 |
271 self.__currentZoom = 0 |
263 |
272 |
264 self.__fileManagerWidget = None |
273 self.__fileManagerWidget = None |
265 self.__chartWidget = None |
274 self.__chartWidget = None |
266 |
275 |
267 self.__unknownPorts = [] |
276 self.__unknownPorts = [] |
268 self.__lastPort = None |
277 self.__lastPort = None |
269 self.__lastDeviceType = None |
278 self.__lastDeviceType = None |
270 |
279 |
271 if HAS_QTSERIALPORT: |
280 if HAS_QTSERIALPORT: |
272 self.__interface = MicroPythonCommandsInterface(self) |
281 self.__interface = MicroPythonCommandsInterface(self) |
273 else: |
282 else: |
274 self.__interface = None |
283 self.__interface = None |
275 self.__device = None |
284 self.__device = None |
276 self.__connected = False |
285 self.__connected = False |
277 self.__setConnected(False) |
286 self.__setConnected(False) |
278 |
287 |
279 if not HAS_QTSERIALPORT: |
288 if not HAS_QTSERIALPORT: |
280 self.replEdit.setHtml(self.tr( |
289 self.replEdit.setHtml( |
281 "<h3>The QtSerialPort package is not available.<br/>" |
290 self.tr( |
282 "MicroPython support is deactivated.</h3>")) |
291 "<h3>The QtSerialPort package is not available.<br/>" |
|
292 "MicroPython support is deactivated.</h3>" |
|
293 ) |
|
294 ) |
283 self.setEnabled(False) |
295 self.setEnabled(False) |
284 return |
296 return |
285 |
297 |
286 self.__vt100Re = re.compile( |
298 self.__vt100Re = re.compile( |
287 r'(?P<count>\d*)(?P<color>(?:;?\d*)*)(?P<action>[ABCDKm])') |
299 r"(?P<count>\d*)(?P<color>(?:;?\d*)*)(?P<action>[ABCDKm])" |
288 |
300 ) |
|
301 |
289 self.__populateDeviceTypeComboBox() |
302 self.__populateDeviceTypeComboBox() |
290 |
303 |
291 self.replEdit.installEventFilter(self) |
304 self.replEdit.installEventFilter(self) |
292 # Hack to intercept middle button paste |
305 # Hack to intercept middle button paste |
293 self.__origReplEditMouseReleaseEvent = self.replEdit.mouseReleaseEvent |
306 self.__origReplEditMouseReleaseEvent = self.replEdit.mouseReleaseEvent |
294 self.replEdit.mouseReleaseEvent = self.__replEditMouseReleaseEvent |
307 self.replEdit.mouseReleaseEvent = self.__replEditMouseReleaseEvent |
295 |
308 |
296 self.replEdit.customContextMenuRequested.connect( |
309 self.replEdit.customContextMenuRequested.connect(self.__showContextMenu) |
297 self.__showContextMenu) |
|
298 self.__ui.preferencesChanged.connect(self.__handlePreferencesChanged) |
310 self.__ui.preferencesChanged.connect(self.__handlePreferencesChanged) |
299 self.__ui.preferencesChanged.connect( |
311 self.__ui.preferencesChanged.connect(self.__interface.handlePreferencesChanged) |
300 self.__interface.handlePreferencesChanged) |
312 |
301 |
|
302 self.__handlePreferencesChanged() |
313 self.__handlePreferencesChanged() |
303 |
314 |
304 charFormat = self.replEdit.currentCharFormat() |
315 charFormat = self.replEdit.currentCharFormat() |
305 self.DefaultForeground = charFormat.foreground() |
316 self.DefaultForeground = charFormat.foreground() |
306 self.DefaultBackground = charFormat.background() |
317 self.DefaultBackground = charFormat.background() |
307 |
318 |
308 def __populateDeviceTypeComboBox(self): |
319 def __populateDeviceTypeComboBox(self): |
309 """ |
320 """ |
310 Private method to populate the device type selector. |
321 Private method to populate the device type selector. |
311 """ |
322 """ |
312 currentDevice = self.deviceTypeComboBox.currentText() |
323 currentDevice = self.deviceTypeComboBox.currentText() |
313 |
324 |
314 self.deviceTypeComboBox.clear() |
325 self.deviceTypeComboBox.clear() |
315 self.deviceInfoLabel.clear() |
326 self.deviceInfoLabel.clear() |
316 |
327 |
317 self.deviceTypeComboBox.addItem("", "") |
328 self.deviceTypeComboBox.addItem("", "") |
318 devices, unknownDevices, unknownPorts = ( |
329 devices, unknownDevices, unknownPorts = MicroPythonDevices.getFoundDevices() |
319 MicroPythonDevices.getFoundDevices() |
|
320 ) |
|
321 if devices: |
330 if devices: |
322 supportedMessage = self.tr( |
331 supportedMessage = self.tr( |
323 "%n supported device(s) detected.", "", len(devices)) |
332 "%n supported device(s) detected.", "", len(devices) |
324 |
333 ) |
325 for index, (boardType, boardName, description, portName, |
334 |
326 vid, pid) in enumerate(sorted(devices), 1): |
335 for index, ( |
|
336 boardType, |
|
337 boardName, |
|
338 description, |
|
339 portName, |
|
340 vid, |
|
341 pid, |
|
342 ) in enumerate(sorted(devices), 1): |
327 self.deviceTypeComboBox.addItem( |
343 self.deviceTypeComboBox.addItem( |
328 self.tr("{0} - {1} ({2})", |
344 self.tr( |
329 "board name, description, port name") |
345 "{0} - {1} ({2})", "board name, description, port name" |
330 .format(boardName, description, portName) |
346 ).format(boardName, description, portName) |
331 ) |
347 ) |
332 self.deviceTypeComboBox.setItemData( |
348 self.deviceTypeComboBox.setItemData( |
333 index, boardType, self.DeviceTypeRole) |
349 index, boardType, self.DeviceTypeRole |
|
350 ) |
334 self.deviceTypeComboBox.setItemData( |
351 self.deviceTypeComboBox.setItemData( |
335 index, boardName, self.DeviceBoardRole) |
352 index, boardName, self.DeviceBoardRole |
|
353 ) |
336 self.deviceTypeComboBox.setItemData( |
354 self.deviceTypeComboBox.setItemData( |
337 index, portName, self.DevicePortRole) |
355 index, portName, self.DevicePortRole |
338 self.deviceTypeComboBox.setItemData( |
356 ) |
339 index, vid, self.DeviceVidRole) |
357 self.deviceTypeComboBox.setItemData(index, vid, self.DeviceVidRole) |
340 self.deviceTypeComboBox.setItemData( |
358 self.deviceTypeComboBox.setItemData(index, pid, self.DevicePidRole) |
341 index, pid, self.DevicePidRole) |
359 |
342 |
|
343 else: |
360 else: |
344 supportedMessage = self.tr("No supported devices detected.") |
361 supportedMessage = self.tr("No supported devices detected.") |
345 |
362 |
346 self.__unknownPorts = unknownPorts |
363 self.__unknownPorts = unknownPorts |
347 if self.__unknownPorts: |
364 if self.__unknownPorts: |
348 unknownMessage = self.tr( |
365 unknownMessage = self.tr( |
349 "\n%n unknown device(s) for manual selection.", "", |
366 "\n%n unknown device(s) for manual selection.", |
350 len(self.__unknownPorts)) |
367 "", |
|
368 len(self.__unknownPorts), |
|
369 ) |
351 if self.deviceTypeComboBox.count(): |
370 if self.deviceTypeComboBox.count(): |
352 self.deviceTypeComboBox.insertSeparator( |
371 self.deviceTypeComboBox.insertSeparator(self.deviceTypeComboBox.count()) |
353 self.deviceTypeComboBox.count()) |
|
354 self.deviceTypeComboBox.addItem(self.tr("Manual Selection")) |
372 self.deviceTypeComboBox.addItem(self.tr("Manual Selection")) |
355 self.deviceTypeComboBox.setItemData( |
373 self.deviceTypeComboBox.setItemData( |
356 self.deviceTypeComboBox.count() - 1, |
374 self.deviceTypeComboBox.count() - 1, |
357 self.ManualMarker, self.DeviceTypeRole) |
375 self.ManualMarker, |
|
376 self.DeviceTypeRole, |
|
377 ) |
358 else: |
378 else: |
359 unknownMessage = "" |
379 unknownMessage = "" |
360 |
380 |
361 self.deviceInfoLabel.setText(supportedMessage + unknownMessage) |
381 self.deviceInfoLabel.setText(supportedMessage + unknownMessage) |
362 |
382 |
363 index = self.deviceTypeComboBox.findText(currentDevice, |
383 index = self.deviceTypeComboBox.findText( |
364 Qt.MatchFlag.MatchExactly) |
384 currentDevice, Qt.MatchFlag.MatchExactly |
|
385 ) |
365 if index == -1: |
386 if index == -1: |
366 # entry is no longer present |
387 # entry is no longer present |
367 index = 0 |
388 index = 0 |
368 if self.__connected: |
389 if self.__connected: |
369 # we are still connected, so disconnect |
390 # we are still connected, so disconnect |
370 self.on_connectButton_clicked() |
391 self.on_connectButton_clicked() |
371 |
392 |
372 self.on_deviceTypeComboBox_activated(index) |
393 self.on_deviceTypeComboBox_activated(index) |
373 self.deviceTypeComboBox.setCurrentIndex(index) |
394 self.deviceTypeComboBox.setCurrentIndex(index) |
374 |
395 |
375 if unknownDevices: |
396 if unknownDevices: |
376 ignoredUnknown = { |
397 ignoredUnknown = { |
377 tuple(d) |
398 tuple(d) for d in Preferences.getMicroPython("IgnoredUnknownDevices") |
378 for d in Preferences.getMicroPython("IgnoredUnknownDevices") |
|
379 } |
399 } |
380 uf2Devices = {(*x[2], x[1]) |
400 uf2Devices = {(*x[2], x[1]) for x in UF2FlashDialog.getFoundDevices()} |
381 for x in UF2FlashDialog.getFoundDevices()} |
401 newUnknownDevices = set(unknownDevices) - ignoredUnknown - uf2Devices |
382 newUnknownDevices = ( |
|
383 set(unknownDevices) - ignoredUnknown - uf2Devices |
|
384 ) |
|
385 if newUnknownDevices: |
402 if newUnknownDevices: |
386 button = EricMessageBox.information( |
403 button = EricMessageBox.information( |
387 self, |
404 self, |
388 self.tr("Unknown MicroPython Device"), |
405 self.tr("Unknown MicroPython Device"), |
389 self.tr( |
406 self.tr( |
390 '<p>Detected these unknown serial devices</p>' |
407 "<p>Detected these unknown serial devices</p>" |
391 '<ul>' |
408 "<ul>" |
392 '<li>{0}</li>' |
409 "<li>{0}</li>" |
393 '</ul>' |
410 "</ul>" |
394 '<p>Please report them together with the board name' |
411 "<p>Please report them together with the board name" |
395 ' and a short description to <a href="mailto:{1}">' |
412 ' and a short description to <a href="mailto:{1}">' |
396 ' the eric bug reporting address</a> if it is a' |
413 " the eric bug reporting address</a> if it is a" |
397 ' MicroPython board.</p>' |
414 " MicroPython board.</p>" |
398 ).format("</li><li>".join([ |
415 ).format( |
399 self.tr("{0} (0x{1:04x}/0x{2:04x})", |
416 "</li><li>".join( |
400 "description, VId, PId").format( |
417 [ |
401 desc, vid, pid) |
418 self.tr( |
402 for vid, pid, desc in newUnknownDevices]), |
419 "{0} (0x{1:04x}/0x{2:04x})", "description, VId, PId" |
403 BugAddress), |
420 ).format(desc, vid, pid) |
404 EricMessageBox.Ignore | EricMessageBox.Ok |
421 for vid, pid, desc in newUnknownDevices |
|
422 ] |
|
423 ), |
|
424 BugAddress, |
|
425 ), |
|
426 EricMessageBox.Ignore | EricMessageBox.Ok, |
405 ) |
427 ) |
406 if button == EricMessageBox.Ignore: |
428 if button == EricMessageBox.Ignore: |
407 ignoredUnknown = list(ignoredUnknown | newUnknownDevices) |
429 ignoredUnknown = list(ignoredUnknown | newUnknownDevices) |
408 Preferences.setMicroPython("IgnoredUnknownDevices", |
430 Preferences.setMicroPython("IgnoredUnknownDevices", ignoredUnknown) |
409 ignoredUnknown) |
|
410 else: |
431 else: |
411 yes = EricMessageBox.yesNo( |
432 yes = EricMessageBox.yesNo( |
412 self, |
433 self, |
413 self.tr("Unknown MicroPython Device"), |
434 self.tr("Unknown MicroPython Device"), |
414 self.tr("""Would you like to add them to the list of""" |
435 self.tr( |
415 """ manually configured devices?"""), |
436 """Would you like to add them to the list of""" |
416 yesDefault=True) |
437 """ manually configured devices?""" |
|
438 ), |
|
439 yesDefault=True, |
|
440 ) |
417 if yes: |
441 if yes: |
418 self.__addUnknownDevices(list(newUnknownDevices)) |
442 self.__addUnknownDevices(list(newUnknownDevices)) |
419 |
443 |
420 def __handlePreferencesChanged(self): |
444 def __handlePreferencesChanged(self): |
421 """ |
445 """ |
422 Private slot to handle a change in preferences. |
446 Private slot to handle a change in preferences. |
423 """ |
447 """ |
424 self.__colorScheme = Preferences.getMicroPython("ColorScheme") |
448 self.__colorScheme = Preferences.getMicroPython("ColorScheme") |
425 |
449 |
426 self.__font = Preferences.getEditorOtherFonts("MonospacedFont") |
450 self.__font = Preferences.getEditorOtherFonts("MonospacedFont") |
427 self.replEdit.setFontFamily(self.__font.family()) |
451 self.replEdit.setFontFamily(self.__font.family()) |
428 self.replEdit.setFontPointSize(self.__font.pointSize()) |
452 self.replEdit.setFontPointSize(self.__font.pointSize()) |
429 |
453 |
430 if Preferences.getMicroPython("ReplLineWrap"): |
454 if Preferences.getMicroPython("ReplLineWrap"): |
431 self.replEdit.setLineWrapMode(QTextEdit.LineWrapMode.WidgetWidth) |
455 self.replEdit.setLineWrapMode(QTextEdit.LineWrapMode.WidgetWidth) |
432 else: |
456 else: |
433 self.replEdit.setLineWrapMode(QTextEdit.LineWrapMode.NoWrap) |
457 self.replEdit.setLineWrapMode(QTextEdit.LineWrapMode.NoWrap) |
434 |
458 |
435 if self.__chartWidget is not None: |
459 if self.__chartWidget is not None: |
436 self.__chartWidget.preferencesChanged() |
460 self.__chartWidget.preferencesChanged() |
437 |
461 |
438 def commandsInterface(self): |
462 def commandsInterface(self): |
439 """ |
463 """ |
440 Public method to get a reference to the commands interface object. |
464 Public method to get a reference to the commands interface object. |
441 |
465 |
442 @return reference to the commands interface object |
466 @return reference to the commands interface object |
443 @rtype MicroPythonCommandsInterface |
467 @rtype MicroPythonCommandsInterface |
444 """ |
468 """ |
445 return self.__interface |
469 return self.__interface |
446 |
470 |
447 def isMicrobit(self): |
471 def isMicrobit(self): |
448 """ |
472 """ |
449 Public method to check, if the connected/selected device is a |
473 Public method to check, if the connected/selected device is a |
450 BBC micro:bit or Calliope mini. |
474 BBC micro:bit or Calliope mini. |
451 |
475 |
452 @return flag indicating a micro:bit device |
476 @return flag indicating a micro:bit device |
453 rtype bool |
477 rtype bool |
454 """ |
478 """ |
455 if self.__device and ( |
479 if self.__device and ( |
456 "micro:bit" in self.__device.deviceName() or |
480 "micro:bit" in self.__device.deviceName() |
457 "Calliope" in self.__device.deviceName() |
481 or "Calliope" in self.__device.deviceName() |
458 ): |
482 ): |
459 return True |
483 return True |
460 |
484 |
461 return False |
485 return False |
462 |
486 |
463 @pyqtSlot(int) |
487 @pyqtSlot(int) |
464 def on_deviceTypeComboBox_activated(self, index): |
488 def on_deviceTypeComboBox_activated(self, index): |
465 """ |
489 """ |
466 Private slot handling the selection of a device type. |
490 Private slot handling the selection of a device type. |
467 |
491 |
468 @param index index of the selected device |
492 @param index index of the selected device |
469 @type int |
493 @type int |
470 """ |
494 """ |
471 deviceType = self.deviceTypeComboBox.itemData( |
495 deviceType = self.deviceTypeComboBox.itemData(index, self.DeviceTypeRole) |
472 index, self.DeviceTypeRole) |
|
473 if deviceType == self.ManualMarker: |
496 if deviceType == self.ManualMarker: |
474 self.connectButton.setEnabled(bool(self.__unknownPorts)) |
497 self.connectButton.setEnabled(bool(self.__unknownPorts)) |
475 else: |
498 else: |
476 self.deviceIconLabel.setPixmap(MicroPythonDevices.getDeviceIcon( |
499 self.deviceIconLabel.setPixmap( |
477 deviceType, False)) |
500 MicroPythonDevices.getDeviceIcon(deviceType, False) |
478 |
501 ) |
479 vid = self.deviceTypeComboBox.itemData( |
502 |
480 index, self.DeviceVidRole) |
503 vid = self.deviceTypeComboBox.itemData(index, self.DeviceVidRole) |
481 pid = self.deviceTypeComboBox.itemData( |
504 pid = self.deviceTypeComboBox.itemData(index, self.DevicePidRole) |
482 index, self.DevicePidRole) |
505 |
483 |
506 self.__device = MicroPythonDevices.getDevice(deviceType, self, vid, pid) |
484 self.__device = MicroPythonDevices.getDevice(deviceType, self, |
|
485 vid, pid) |
|
486 self.__device.setButtons() |
507 self.__device.setButtons() |
487 |
508 |
488 self.connectButton.setEnabled(bool(deviceType)) |
509 self.connectButton.setEnabled(bool(deviceType)) |
489 |
510 |
490 @pyqtSlot() |
511 @pyqtSlot() |
491 def on_checkButton_clicked(self): |
512 def on_checkButton_clicked(self): |
492 """ |
513 """ |
493 Private slot to check for connected devices. |
514 Private slot to check for connected devices. |
494 """ |
515 """ |
495 self.__populateDeviceTypeComboBox() |
516 self.__populateDeviceTypeComboBox() |
496 |
517 |
497 def setActionButtons(self, **kwargs): |
518 def setActionButtons(self, **kwargs): |
498 """ |
519 """ |
499 Public method to set the enabled state of the various action buttons. |
520 Public method to set the enabled state of the various action buttons. |
500 |
521 |
501 @keyparam kwargs keyword arguments containg the enabled states (keys |
522 @keyparam kwargs keyword arguments containg the enabled states (keys |
502 are 'run', 'repl', 'files', 'chart', 'open', 'save' |
523 are 'run', 'repl', 'files', 'chart', 'open', 'save' |
503 @type dict |
524 @type dict |
504 """ |
525 """ |
505 if "open" in kwargs: |
526 if "open" in kwargs: |
529 selectAllKeys = QKeySequence("Ctrl+A") |
550 selectAllKeys = QKeySequence("Ctrl+A") |
530 else: |
551 else: |
531 copyKeys = QKeySequence("Ctrl+Shift+C") |
552 copyKeys = QKeySequence("Ctrl+Shift+C") |
532 pasteKeys = QKeySequence("Ctrl+Shift+V") |
553 pasteKeys = QKeySequence("Ctrl+Shift+V") |
533 selectAllKeys = QKeySequence("Ctrl+Shift+A") |
554 selectAllKeys = QKeySequence("Ctrl+Shift+A") |
534 |
555 |
535 menu = QMenu(self) |
556 menu = QMenu(self) |
536 act = menu.addAction( |
557 act = menu.addAction( |
537 UI.PixmapCache.getIcon("editDelete"), self.tr("Clear"), |
558 UI.PixmapCache.getIcon("editDelete"), self.tr("Clear"), self.__clear |
538 self.__clear) |
559 ) |
539 act.setEnabled(bool(self.replEdit.toPlainText())) |
560 act.setEnabled(bool(self.replEdit.toPlainText())) |
540 menu.addSeparator() |
561 menu.addSeparator() |
541 act = menu.addAction( |
562 act = menu.addAction( |
542 UI.PixmapCache.getIcon("editCopy"), self.tr("Copy"), |
563 UI.PixmapCache.getIcon("editCopy"), |
543 copyKeys, self.replEdit.copy) |
564 self.tr("Copy"), |
|
565 copyKeys, |
|
566 self.replEdit.copy, |
|
567 ) |
544 act.setEnabled(self.replEdit.textCursor().hasSelection()) |
568 act.setEnabled(self.replEdit.textCursor().hasSelection()) |
545 act = menu.addAction( |
569 act = menu.addAction( |
546 UI.PixmapCache.getIcon("editPaste"), self.tr("Paste"), |
570 UI.PixmapCache.getIcon("editPaste"), |
547 pasteKeys, self.__paste) |
571 self.tr("Paste"), |
548 act.setEnabled(self.replEdit.canPaste() and |
572 pasteKeys, |
549 self.__interface.isConnected()) |
573 self.__paste, |
|
574 ) |
|
575 act.setEnabled(self.replEdit.canPaste() and self.__interface.isConnected()) |
550 menu.addSeparator() |
576 menu.addSeparator() |
551 act = menu.addAction( |
577 act = menu.addAction( |
552 UI.PixmapCache.getIcon("editSelectAll"), self.tr("Select All"), |
578 UI.PixmapCache.getIcon("editSelectAll"), |
553 selectAllKeys, self.replEdit.selectAll) |
579 self.tr("Select All"), |
|
580 selectAllKeys, |
|
581 self.replEdit.selectAll, |
|
582 ) |
554 act.setEnabled(bool(self.replEdit.toPlainText())) |
583 act.setEnabled(bool(self.replEdit.toPlainText())) |
555 |
584 |
556 menu.exec(self.replEdit.mapToGlobal(pos)) |
585 menu.exec(self.replEdit.mapToGlobal(pos)) |
557 |
586 |
558 def __setConnected(self, connected): |
587 def __setConnected(self, connected): |
559 """ |
588 """ |
560 Private method to set the connection status LED. |
589 Private method to set the connection status LED. |
561 |
590 |
562 @param connected connection state |
591 @param connected connection state |
563 @type bool |
592 @type bool |
564 """ |
593 """ |
565 self.__connected = connected |
594 self.__connected = connected |
566 |
595 |
567 self.deviceConnectedLed.setOn(connected) |
596 self.deviceConnectedLed.setOn(connected) |
568 if self.__fileManagerWidget: |
597 if self.__fileManagerWidget: |
569 self.__fileManagerWidget.deviceConnectedLed.setOn(connected) |
598 self.__fileManagerWidget.deviceConnectedLed.setOn(connected) |
570 |
599 |
571 self.deviceTypeComboBox.setEnabled(not connected) |
600 self.deviceTypeComboBox.setEnabled(not connected) |
572 |
601 |
573 if connected: |
602 if connected: |
574 self.connectButton.setIcon( |
603 self.connectButton.setIcon(UI.PixmapCache.getIcon("linkDisconnect")) |
575 UI.PixmapCache.getIcon("linkDisconnect")) |
604 self.connectButton.setToolTip( |
576 self.connectButton.setToolTip(self.tr( |
605 self.tr("Press to disconnect the current device") |
577 "Press to disconnect the current device")) |
606 ) |
578 else: |
607 else: |
579 self.connectButton.setIcon( |
608 self.connectButton.setIcon(UI.PixmapCache.getIcon("linkConnect")) |
580 UI.PixmapCache.getIcon("linkConnect")) |
609 self.connectButton.setToolTip( |
581 self.connectButton.setToolTip(self.tr( |
610 self.tr("Press to connect the selected device") |
582 "Press to connect the selected device")) |
611 ) |
583 |
612 |
584 def isConnected(self): |
613 def isConnected(self): |
585 """ |
614 """ |
586 Public method to get the connection state. |
615 Public method to get the connection state. |
587 |
616 |
588 @return connection state |
617 @return connection state |
589 @rtype bool |
618 @rtype bool |
590 """ |
619 """ |
591 return self.__connected |
620 return self.__connected |
592 |
621 |
593 def __showNoDeviceMessage(self): |
622 def __showNoDeviceMessage(self): |
594 """ |
623 """ |
595 Private method to show a message dialog indicating a missing device. |
624 Private method to show a message dialog indicating a missing device. |
596 """ |
625 """ |
597 EricMessageBox.critical( |
626 EricMessageBox.critical( |
598 self, |
627 self, |
599 self.tr("No device attached"), |
628 self.tr("No device attached"), |
600 self.tr("""Please ensure the device is plugged into your""" |
629 self.tr( |
601 """ computer and selected.\n\nIt must have a version""" |
630 """Please ensure the device is plugged into your""" |
602 """ of MicroPython (or CircuitPython) flashed onto""" |
631 """ computer and selected.\n\nIt must have a version""" |
603 """ it before anything will work.\n\nFinally press""" |
632 """ of MicroPython (or CircuitPython) flashed onto""" |
604 """ the device's reset button and wait a few seconds""" |
633 """ it before anything will work.\n\nFinally press""" |
605 """ before trying again.""")) |
634 """ the device's reset button and wait a few seconds""" |
606 |
635 """ before trying again.""" |
|
636 ), |
|
637 ) |
|
638 |
607 @pyqtSlot(bool) |
639 @pyqtSlot(bool) |
608 def on_replButton_clicked(self, checked): |
640 def on_replButton_clicked(self, checked): |
609 """ |
641 """ |
610 Private slot to connect to enable or disable the REPL widget. |
642 Private slot to connect to enable or disable the REPL widget. |
611 |
643 |
612 If the selected device is not connected yet, this will be done now. |
644 If the selected device is not connected yet, this will be done now. |
613 |
645 |
614 @param checked state of the button |
646 @param checked state of the button |
615 @type bool |
647 @type bool |
616 """ |
648 """ |
617 if not self.__device: |
649 if not self.__device: |
618 self.__showNoDeviceMessage() |
650 self.__showNoDeviceMessage() |
619 return |
651 return |
620 |
652 |
621 if checked: |
653 if checked: |
622 ok, reason = self.__device.canStartRepl() |
654 ok, reason = self.__device.canStartRepl() |
623 if not ok: |
655 if not ok: |
624 EricMessageBox.warning( |
656 EricMessageBox.warning( |
625 self, |
657 self, |
626 self.tr("Start REPL"), |
658 self.tr("Start REPL"), |
627 self.tr("""<p>The REPL cannot be started.</p><p>Reason:""" |
659 self.tr( |
628 """ {0}</p>""").format(reason)) |
660 """<p>The REPL cannot be started.</p><p>Reason:""" |
|
661 """ {0}</p>""" |
|
662 ).format(reason), |
|
663 ) |
629 return |
664 return |
630 |
665 |
631 self.replEdit.clear() |
666 self.replEdit.clear() |
632 self.__interface.dataReceived.connect(self.__processData) |
667 self.__interface.dataReceived.connect(self.__processData) |
633 |
668 |
634 if not self.__interface.isConnected(): |
669 if not self.__interface.isConnected(): |
635 self.__connectToDevice() |
670 self.__connectToDevice() |
636 if self.__device.forceInterrupt(): |
671 if self.__device.forceInterrupt(): |
637 # send a Ctrl-B (exit raw mode) |
672 # send a Ctrl-B (exit raw mode) |
638 self.__interface.write(b'\x02') |
673 self.__interface.write(b"\x02") |
639 # send Ctrl-C (keyboard interrupt) |
674 # send Ctrl-C (keyboard interrupt) |
640 self.__interface.write(b'\x03') |
675 self.__interface.write(b"\x03") |
641 |
676 |
642 self.__device.setRepl(True) |
677 self.__device.setRepl(True) |
643 self.replEdit.setFocus(Qt.FocusReason.OtherFocusReason) |
678 self.replEdit.setFocus(Qt.FocusReason.OtherFocusReason) |
644 else: |
679 else: |
645 self.__interface.dataReceived.disconnect(self.__processData) |
680 self.__interface.dataReceived.disconnect(self.__processData) |
646 if (not self.chartButton.isChecked() and |
681 if not self.chartButton.isChecked() and not self.filesButton.isChecked(): |
647 not self.filesButton.isChecked()): |
|
648 self.__disconnectFromDevice() |
682 self.__disconnectFromDevice() |
649 self.__device.setRepl(False) |
683 self.__device.setRepl(False) |
650 self.replButton.setChecked(checked) |
684 self.replButton.setChecked(checked) |
651 |
685 |
652 @pyqtSlot() |
686 @pyqtSlot() |
653 def on_connectButton_clicked(self): |
687 def on_connectButton_clicked(self): |
654 """ |
688 """ |
655 Private slot to connect to the selected device or disconnect from the |
689 Private slot to connect to the selected device or disconnect from the |
656 currently connected device. |
690 currently connected device. |
657 """ |
691 """ |
658 if self.__connected: |
692 if self.__connected: |
659 with EricOverrideCursor(): |
693 with EricOverrideCursor(): |
660 self.__disconnectFromDevice() |
694 self.__disconnectFromDevice() |
661 |
695 |
662 if self.replButton.isChecked(): |
696 if self.replButton.isChecked(): |
663 self.on_replButton_clicked(False) |
697 self.on_replButton_clicked(False) |
664 if self.filesButton.isChecked(): |
698 if self.filesButton.isChecked(): |
665 self.on_filesButton_clicked(False) |
699 self.on_filesButton_clicked(False) |
666 if self.chartButton.isChecked(): |
700 if self.chartButton.isChecked(): |
667 self.on_chartButton_clicked(False) |
701 self.on_chartButton_clicked(False) |
668 else: |
702 else: |
669 with EricOverrideCursor(): |
703 with EricOverrideCursor(): |
670 self.__connectToDevice() |
704 self.__connectToDevice() |
671 |
705 |
672 @pyqtSlot() |
706 @pyqtSlot() |
673 def __clear(self): |
707 def __clear(self): |
674 """ |
708 """ |
675 Private slot to clear the REPL pane. |
709 Private slot to clear the REPL pane. |
676 """ |
710 """ |
677 self.replEdit.clear() |
711 self.replEdit.clear() |
678 self.__interface.isConnected() and self.__interface.write(b"\r") |
712 self.__interface.isConnected() and self.__interface.write(b"\r") |
679 |
713 |
680 @pyqtSlot() |
714 @pyqtSlot() |
681 def __paste(self, mode=QClipboard.Mode.Clipboard): |
715 def __paste(self, mode=QClipboard.Mode.Clipboard): |
682 """ |
716 """ |
683 Private slot to perform a paste operation. |
717 Private slot to perform a paste operation. |
684 |
718 |
685 @param mode paste mode (defaults to QClipboard.Mode.Clipboard) |
719 @param mode paste mode (defaults to QClipboard.Mode.Clipboard) |
686 @type QClipboard.Mode (optional) |
720 @type QClipboard.Mode (optional) |
687 """ |
721 """ |
688 # add support for paste by mouse middle button |
722 # add support for paste by mouse middle button |
689 clipboard = QApplication.clipboard() |
723 clipboard = QApplication.clipboard() |
690 if clipboard: |
724 if clipboard: |
691 pasteText = clipboard.text(mode=mode) |
725 pasteText = clipboard.text(mode=mode) |
692 if pasteText: |
726 if pasteText: |
693 pasteText = pasteText.replace('\n\r', '\r') |
727 pasteText = pasteText.replace("\n\r", "\r") |
694 pasteText = pasteText.replace('\n', '\r') |
728 pasteText = pasteText.replace("\n", "\r") |
695 self.__interface.isConnected() and self.__interface.write( |
729 self.__interface.isConnected() and self.__interface.write( |
696 pasteText.encode("utf-8")) |
730 pasteText.encode("utf-8") |
697 |
731 ) |
|
732 |
698 def eventFilter(self, obj, evt): |
733 def eventFilter(self, obj, evt): |
699 """ |
734 """ |
700 Public method to process events for the REPL pane. |
735 Public method to process events for the REPL pane. |
701 |
736 |
702 @param obj reference to the object the event was meant for |
737 @param obj reference to the object the event was meant for |
703 @type QObject |
738 @type QObject |
704 @param evt reference to the event object |
739 @param evt reference to the event object |
705 @type QEvent |
740 @type QEvent |
706 @return flag to indicate that the event was handled |
741 @return flag to indicate that the event was handled |
707 @rtype bool |
742 @rtype bool |
708 """ |
743 """ |
709 if obj is self.replEdit and evt.type() == QEvent.Type.KeyPress: |
744 if obj is self.replEdit and evt.type() == QEvent.Type.KeyPress: |
710 # handle the key press event on behalve of the REPL pane |
745 # handle the key press event on behalve of the REPL pane |
711 key = evt.key() |
746 key = evt.key() |
712 msg = bytes(evt.text(), 'utf8') |
747 msg = bytes(evt.text(), "utf8") |
713 if key == Qt.Key.Key_Backspace: |
748 if key == Qt.Key.Key_Backspace: |
714 msg = b'\b' |
749 msg = b"\b" |
715 elif key == Qt.Key.Key_Delete: |
750 elif key == Qt.Key.Key_Delete: |
716 msg = b'\x1B[\x33\x7E' |
751 msg = b"\x1B[\x33\x7E" |
717 elif key == Qt.Key.Key_Up: |
752 elif key == Qt.Key.Key_Up: |
718 msg = b'\x1B[A' |
753 msg = b"\x1B[A" |
719 elif key == Qt.Key.Key_Down: |
754 elif key == Qt.Key.Key_Down: |
720 msg = b'\x1B[B' |
755 msg = b"\x1B[B" |
721 elif key == Qt.Key.Key_Right: |
756 elif key == Qt.Key.Key_Right: |
722 msg = b'\x1B[C' |
757 msg = b"\x1B[C" |
723 elif key == Qt.Key.Key_Left: |
758 elif key == Qt.Key.Key_Left: |
724 msg = b'\x1B[D' |
759 msg = b"\x1B[D" |
725 elif key == Qt.Key.Key_Home: |
760 elif key == Qt.Key.Key_Home: |
726 msg = b'\x1B[H' |
761 msg = b"\x1B[H" |
727 elif key == Qt.Key.Key_End: |
762 elif key == Qt.Key.Key_End: |
728 msg = b'\x1B[F' |
763 msg = b"\x1B[F" |
729 elif ((Globals.isMacPlatform() and |
764 elif ( |
730 evt.modifiers() == Qt.KeyboardModifier.MetaModifier) or |
765 Globals.isMacPlatform() |
731 (not Globals.isMacPlatform() and |
766 and evt.modifiers() == Qt.KeyboardModifier.MetaModifier |
732 evt.modifiers() == Qt.KeyboardModifier.ControlModifier)): |
767 ) or ( |
|
768 not Globals.isMacPlatform() |
|
769 and evt.modifiers() == Qt.KeyboardModifier.ControlModifier |
|
770 ): |
733 if Qt.Key.Key_A <= key <= Qt.Key.Key_Z: |
771 if Qt.Key.Key_A <= key <= Qt.Key.Key_Z: |
734 # devices treat an input of \x01 as Ctrl+A, etc. |
772 # devices treat an input of \x01 as Ctrl+A, etc. |
735 msg = bytes([1 + key - Qt.Key.Key_A]) |
773 msg = bytes([1 + key - Qt.Key.Key_A]) |
736 elif ( |
774 elif evt.modifiers() == ( |
737 evt.modifiers() == ( |
775 Qt.KeyboardModifier.ControlModifier | Qt.KeyboardModifier.ShiftModifier |
738 Qt.KeyboardModifier.ControlModifier | |
776 ) or ( |
739 Qt.KeyboardModifier.ShiftModifier) or |
777 Globals.isMacPlatform() |
740 (Globals.isMacPlatform() and |
778 and evt.modifiers() == Qt.KeyboardModifier.ControlModifier |
741 evt.modifiers() == Qt.KeyboardModifier.ControlModifier) |
|
742 ): |
779 ): |
743 if key == Qt.Key.Key_C: |
780 if key == Qt.Key.Key_C: |
744 self.replEdit.copy() |
781 self.replEdit.copy() |
745 msg = b'' |
782 msg = b"" |
746 elif key == Qt.Key.Key_V: |
783 elif key == Qt.Key.Key_V: |
747 self.__paste() |
784 self.__paste() |
748 msg = b'' |
785 msg = b"" |
749 elif key == Qt.Key.Key_A: |
786 elif key == Qt.Key.Key_A: |
750 self.replEdit.selectAll() |
787 self.replEdit.selectAll() |
751 msg = b"" |
788 msg = b"" |
752 elif key in (Qt.Key.Key_Return, Qt.Key.Key_Enter): |
789 elif key in (Qt.Key.Key_Return, Qt.Key.Key_Enter): |
753 tc = self.replEdit.textCursor() |
790 tc = self.replEdit.textCursor() |
756 self.__interface.isConnected() and self.__interface.write(msg) |
793 self.__interface.isConnected() and self.__interface.write(msg) |
757 return True |
794 return True |
758 else: |
795 else: |
759 # standard event processing |
796 # standard event processing |
760 return super().eventFilter(obj, evt) |
797 return super().eventFilter(obj, evt) |
761 |
798 |
762 def __replEditMouseReleaseEvent(self, evt): |
799 def __replEditMouseReleaseEvent(self, evt): |
763 """ |
800 """ |
764 Private method handling mouse release events for the replEdit widget. |
801 Private method handling mouse release events for the replEdit widget. |
765 |
802 |
766 Note: this is a hack because QTextEdit does not allow filtering of |
803 Note: this is a hack because QTextEdit does not allow filtering of |
767 QEvent.Type.MouseButtonRelease. To make middle button paste work, we |
804 QEvent.Type.MouseButtonRelease. To make middle button paste work, we |
768 had to intercept the protected event method (some kind of |
805 had to intercept the protected event method (some kind of |
769 reimplementing it). |
806 reimplementing it). |
770 |
807 |
771 @param evt reference to the event object |
808 @param evt reference to the event object |
772 @type QMouseEvent |
809 @type QMouseEvent |
773 """ |
810 """ |
774 if evt.button() == Qt.MouseButton.MiddleButton: |
811 if evt.button() == Qt.MouseButton.MiddleButton: |
775 self.__paste(mode=QClipboard.Mode.Selection) |
812 self.__paste(mode=QClipboard.Mode.Selection) |
776 msg = b'' |
813 msg = b"" |
777 if self.__interface.isConnected(): |
814 if self.__interface.isConnected(): |
778 self.__interface.write(msg) |
815 self.__interface.write(msg) |
779 evt.accept() |
816 evt.accept() |
780 else: |
817 else: |
781 self.__origReplEditMouseReleaseEvent(evt) |
818 self.__origReplEditMouseReleaseEvent(evt) |
782 |
819 |
783 def __processData(self, data): |
820 def __processData(self, data): |
784 """ |
821 """ |
785 Private slot to process bytes received from the device. |
822 Private slot to process bytes received from the device. |
786 |
823 |
787 @param data bytes received from the device |
824 @param data bytes received from the device |
788 @type bytes |
825 @type bytes |
789 """ |
826 """ |
790 tc = self.replEdit.textCursor() |
827 tc = self.replEdit.textCursor() |
791 # the text cursor must be on the last line |
828 # the text cursor must be on the last line |
792 while tc.movePosition(QTextCursor.MoveOperation.Down): |
829 while tc.movePosition(QTextCursor.MoveOperation.Down): |
793 pass |
830 pass |
794 |
831 |
795 # set the font |
832 # set the font |
796 charFormat = tc.charFormat() |
833 charFormat = tc.charFormat() |
797 charFormat.setFontFamilies([self.__font.family()]) |
834 charFormat.setFontFamilies([self.__font.family()]) |
798 charFormat.setFontPointSize(self.__font.pointSize()) |
835 charFormat.setFontPointSize(self.__font.pointSize()) |
799 tc.setCharFormat(charFormat) |
836 tc.setCharFormat(charFormat) |
800 |
837 |
801 index = 0 |
838 index = 0 |
802 while index < len(data): |
839 while index < len(data): |
803 if data[index] == 8: # \b |
840 if data[index] == 8: # \b |
804 tc.movePosition(QTextCursor.MoveOperation.Left) |
841 tc.movePosition(QTextCursor.MoveOperation.Left) |
805 self.replEdit.setTextCursor(tc) |
842 self.replEdit.setTextCursor(tc) |
806 elif data[index] in (4, 13): # EOT, \r |
843 elif data[index] in (4, 13): # EOT, \r |
807 pass |
844 pass |
808 elif (len(data) > index + 1 and |
845 elif len(data) > index + 1 and data[index] == 27 and data[index + 1] == 91: |
809 data[index] == 27 and |
|
810 data[index + 1] == 91): |
|
811 # VT100 cursor command detected: <Esc>[ |
846 # VT100 cursor command detected: <Esc>[ |
812 index += 2 # move index to after the [ |
847 index += 2 # move index to after the [ |
813 match = self.__vt100Re.search( |
848 match = self.__vt100Re.search( |
814 data[index:].decode("utf-8", errors="replace")) |
849 data[index:].decode("utf-8", errors="replace") |
|
850 ) |
815 if match: |
851 if match: |
816 # move to last position in control sequence |
852 # move to last position in control sequence |
817 # ++ will be done at end of loop |
853 # ++ will be done at end of loop |
818 index += match.end() - 1 |
854 index += match.end() - 1 |
819 |
855 |
820 action = match.group("action") |
856 action = match.group("action") |
821 if action in "ABCD": |
857 if action in "ABCD": |
822 if match.group("count") == "": |
858 if match.group("count") == "": |
823 count = 1 |
859 count = 1 |
824 else: |
860 else: |
825 count = int(match.group("count")) |
861 count = int(match.group("count")) |
826 |
862 |
827 if action == "A": # up |
863 if action == "A": # up |
828 tc.movePosition(QTextCursor.MoveOperation.Up, |
864 tc.movePosition(QTextCursor.MoveOperation.Up, n=count) |
829 n=count) |
|
830 self.replEdit.setTextCursor(tc) |
865 self.replEdit.setTextCursor(tc) |
831 elif action == "B": # down |
866 elif action == "B": # down |
832 tc.movePosition(QTextCursor.MoveOperation.Down, |
867 tc.movePosition(QTextCursor.MoveOperation.Down, n=count) |
833 n=count) |
|
834 self.replEdit.setTextCursor(tc) |
868 self.replEdit.setTextCursor(tc) |
835 elif action == "C": # right |
869 elif action == "C": # right |
836 tc.movePosition(QTextCursor.MoveOperation.Right, |
870 tc.movePosition(QTextCursor.MoveOperation.Right, n=count) |
837 n=count) |
|
838 self.replEdit.setTextCursor(tc) |
871 self.replEdit.setTextCursor(tc) |
839 elif action == "D": # left |
872 elif action == "D": # left |
840 tc.movePosition(QTextCursor.MoveOperation.Left, |
873 tc.movePosition(QTextCursor.MoveOperation.Left, n=count) |
841 n=count) |
|
842 self.replEdit.setTextCursor(tc) |
874 self.replEdit.setTextCursor(tc) |
843 elif action == "K": # delete things |
875 elif action == "K": # delete things |
844 if match.group("count") in ("", "0"): |
876 if match.group("count") in ("", "0"): |
845 # delete to end of line |
877 # delete to end of line |
846 tc.movePosition( |
878 tc.movePosition( |
847 QTextCursor.MoveOperation.EndOfLine, |
879 QTextCursor.MoveOperation.EndOfLine, |
848 mode=QTextCursor.MoveMode.KeepAnchor) |
880 mode=QTextCursor.MoveMode.KeepAnchor, |
|
881 ) |
849 tc.removeSelectedText() |
882 tc.removeSelectedText() |
850 self.replEdit.setTextCursor(tc) |
883 self.replEdit.setTextCursor(tc) |
851 elif match.group("count") == "1": |
884 elif match.group("count") == "1": |
852 # delete to beinning of line |
885 # delete to beinning of line |
853 tc.movePosition( |
886 tc.movePosition( |
854 QTextCursor.MoveOperation.StartOfLine, |
887 QTextCursor.MoveOperation.StartOfLine, |
855 mode=QTextCursor.MoveMode.KeepAnchor) |
888 mode=QTextCursor.MoveMode.KeepAnchor, |
|
889 ) |
856 tc.removeSelectedText() |
890 tc.removeSelectedText() |
857 self.replEdit.setTextCursor(tc) |
891 self.replEdit.setTextCursor(tc) |
858 elif match.group("count") == "2": |
892 elif match.group("count") == "2": |
859 # delete whole line |
893 # delete whole line |
860 tc.movePosition( |
894 tc.movePosition(QTextCursor.MoveOperation.EndOfLine) |
861 QTextCursor.MoveOperation.EndOfLine) |
|
862 tc.movePosition( |
895 tc.movePosition( |
863 QTextCursor.MoveOperation.StartOfLine, |
896 QTextCursor.MoveOperation.StartOfLine, |
864 mode=QTextCursor.MoveMode.KeepAnchor) |
897 mode=QTextCursor.MoveMode.KeepAnchor, |
|
898 ) |
865 tc.removeSelectedText() |
899 tc.removeSelectedText() |
866 self.replEdit.setTextCursor(tc) |
900 self.replEdit.setTextCursor(tc) |
867 elif action == "m": |
901 elif action == "m": |
868 self.__setCharFormat(match.group(0)[:-1].split(";"), |
902 self.__setCharFormat(match.group(0)[:-1].split(";"), tc) |
869 tc) |
|
870 else: |
903 else: |
871 tc.deleteChar() |
904 tc.deleteChar() |
872 self.replEdit.setTextCursor(tc) |
905 self.replEdit.setTextCursor(tc) |
873 self.replEdit.insertPlainText(chr(data[index])) |
906 self.replEdit.insertPlainText(chr(data[index])) |
874 |
907 |
875 index += 1 |
908 index += 1 |
876 |
909 |
877 self.replEdit.ensureCursorVisible() |
910 self.replEdit.ensureCursorVisible() |
878 |
911 |
879 def __setCharFormat(self, formatCodes, textCursor): |
912 def __setCharFormat(self, formatCodes, textCursor): |
880 """ |
913 """ |
881 Private method setting the current text format of the REPL pane based |
914 Private method setting the current text format of the REPL pane based |
882 on the passed ANSI codes. |
915 on the passed ANSI codes. |
883 |
916 |
884 Following codes are used: |
917 Following codes are used: |
885 <ul> |
918 <ul> |
886 <li>0: Reset</li> |
919 <li>0: Reset</li> |
887 <li>1: Bold font (weight 75)</li> |
920 <li>1: Bold font (weight 75)</li> |
888 <li>2: Light font (weight 25)</li> |
921 <li>2: Light font (weight 25)</li> |
1026 else: |
1063 else: |
1027 # return with device path prepended |
1064 # return with device path prepended |
1028 return "/dev/{0}".format(portName) |
1065 return "/dev/{0}".format(portName) |
1029 else: |
1066 else: |
1030 return "" |
1067 return "" |
1031 |
1068 |
1032 def getCurrentBoard(self): |
1069 def getCurrentBoard(self): |
1033 """ |
1070 """ |
1034 Public method to get the board name of the selected device. |
1071 Public method to get the board name of the selected device. |
1035 |
1072 |
1036 @return board name of the selected device |
1073 @return board name of the selected device |
1037 @rtype str |
1074 @rtype str |
1038 """ |
1075 """ |
1039 boardName = self.deviceTypeComboBox.currentData(self.DeviceBoardRole) |
1076 boardName = self.deviceTypeComboBox.currentData(self.DeviceBoardRole) |
1040 return boardName |
1077 return boardName |
1041 |
1078 |
1042 def getDeviceWorkspace(self): |
1079 def getDeviceWorkspace(self): |
1043 """ |
1080 """ |
1044 Public method to get the workspace directory of the device. |
1081 Public method to get the workspace directory of the device. |
1045 |
1082 |
1046 @return workspace directory of the device |
1083 @return workspace directory of the device |
1047 @rtype str |
1084 @rtype str |
1048 """ |
1085 """ |
1049 if self.__device: |
1086 if self.__device: |
1050 return self.__device.getWorkspace() |
1087 return self.__device.getWorkspace() |
1051 else: |
1088 else: |
1052 return "" |
1089 return "" |
1053 |
1090 |
1054 def __connectToDevice(self): |
1091 def __connectToDevice(self): |
1055 """ |
1092 """ |
1056 Private method to connect to the selected device. |
1093 Private method to connect to the selected device. |
1057 """ |
1094 """ |
1058 port = self.getCurrentPort() |
1095 port = self.getCurrentPort() |
1059 if not port: |
1096 if not port: |
1060 from .ConnectionSelectionDialog import ConnectionSelectionDialog |
1097 from .ConnectionSelectionDialog import ConnectionSelectionDialog |
|
1098 |
1061 with EricOverridenCursor(): |
1099 with EricOverridenCursor(): |
1062 dlg = ConnectionSelectionDialog( |
1100 dlg = ConnectionSelectionDialog( |
1063 self.__unknownPorts, self.__lastPort, self.__lastDeviceType |
1101 self.__unknownPorts, self.__lastPort, self.__lastDeviceType |
1064 ) |
1102 ) |
1065 if dlg.exec() == QDialog.DialogCode.Accepted: |
1103 if dlg.exec() == QDialog.DialogCode.Accepted: |
1066 vid, pid, port, deviceType = dlg.getData() |
1104 vid, pid, port, deviceType = dlg.getData() |
1067 |
1105 |
1068 self.deviceIconLabel.setPixmap( |
1106 self.deviceIconLabel.setPixmap( |
1069 MicroPythonDevices.getDeviceIcon(deviceType, False)) |
1107 MicroPythonDevices.getDeviceIcon(deviceType, False) |
|
1108 ) |
1070 self.__device = MicroPythonDevices.getDevice( |
1109 self.__device = MicroPythonDevices.getDevice( |
1071 deviceType, self, vid, pid) |
1110 deviceType, self, vid, pid |
|
1111 ) |
1072 self.__device.setButtons() |
1112 self.__device.setButtons() |
1073 |
1113 |
1074 self.__lastPort = port |
1114 self.__lastPort = port |
1075 self.__lastDeviceType = deviceType |
1115 self.__lastDeviceType = deviceType |
1076 else: |
1116 else: |
1077 return |
1117 return |
1078 |
1118 |
1079 if self.__interface.connectToDevice(port): |
1119 if self.__interface.connectToDevice(port): |
1080 self.__setConnected(True) |
1120 self.__setConnected(True) |
1081 |
1121 |
1082 if (Preferences.getMicroPython("SyncTimeAfterConnect") and |
1122 if ( |
1083 self.__device.hasTimeCommands()): |
1123 Preferences.getMicroPython("SyncTimeAfterConnect") |
|
1124 and self.__device.hasTimeCommands() |
|
1125 ): |
1084 self.__synchronizeTime(quiet=True) |
1126 self.__synchronizeTime(quiet=True) |
1085 else: |
1127 else: |
1086 with EricOverridenCursor(): |
1128 with EricOverridenCursor(): |
1087 EricMessageBox.warning( |
1129 EricMessageBox.warning( |
1088 self, |
1130 self, |
1089 self.tr("Serial Device Connect"), |
1131 self.tr("Serial Device Connect"), |
1090 self.tr("""<p>Cannot connect to device at serial""" |
1132 self.tr( |
1091 """ port <b>{0}</b>.</p>""").format(port)) |
1133 """<p>Cannot connect to device at serial""" |
1092 |
1134 """ port <b>{0}</b>.</p>""" |
|
1135 ).format(port), |
|
1136 ) |
|
1137 |
1093 def __disconnectFromDevice(self): |
1138 def __disconnectFromDevice(self): |
1094 """ |
1139 """ |
1095 Private method to disconnect from the device. |
1140 Private method to disconnect from the device. |
1096 """ |
1141 """ |
1097 self.__interface.disconnectFromDevice() |
1142 self.__interface.disconnectFromDevice() |
1098 self.__setConnected(False) |
1143 self.__setConnected(False) |
1099 |
1144 |
1100 @pyqtSlot() |
1145 @pyqtSlot() |
1101 def on_runButton_clicked(self): |
1146 def on_runButton_clicked(self): |
1102 """ |
1147 """ |
1103 Private slot to execute the script of the active editor on the |
1148 Private slot to execute the script of the active editor on the |
1104 selected device. |
1149 selected device. |
1105 |
1150 |
1106 If the REPL is not active yet, it will be activated, which might cause |
1151 If the REPL is not active yet, it will be activated, which might cause |
1107 an unconnected device to be connected. |
1152 an unconnected device to be connected. |
1108 """ |
1153 """ |
1109 if not self.__device: |
1154 if not self.__device: |
1110 self.__showNoDeviceMessage() |
1155 self.__showNoDeviceMessage() |
1111 return |
1156 return |
1112 |
1157 |
1113 aw = ericApp().getObject("ViewManager").activeWindow() |
1158 aw = ericApp().getObject("ViewManager").activeWindow() |
1114 if aw is None: |
1159 if aw is None: |
1115 EricMessageBox.critical( |
1160 EricMessageBox.critical( |
1116 self, |
1161 self, |
1117 self.tr("Run Script"), |
1162 self.tr("Run Script"), |
1118 self.tr("""There is no editor open. Abort...""")) |
1163 self.tr("""There is no editor open. Abort..."""), |
|
1164 ) |
1119 return |
1165 return |
1120 |
1166 |
1121 script = aw.text() |
1167 script = aw.text() |
1122 if not script: |
1168 if not script: |
1123 EricMessageBox.critical( |
1169 EricMessageBox.critical( |
1124 self, |
1170 self, |
1125 self.tr("Run Script"), |
1171 self.tr("Run Script"), |
1126 self.tr("""The current editor does not contain a script.""" |
1172 self.tr( |
1127 """ Abort...""")) |
1173 """The current editor does not contain a script.""" """ Abort...""" |
|
1174 ), |
|
1175 ) |
1128 return |
1176 return |
1129 |
1177 |
1130 ok, reason = self.__device.canRunScript() |
1178 ok, reason = self.__device.canRunScript() |
1131 if not ok: |
1179 if not ok: |
1132 EricMessageBox.warning( |
1180 EricMessageBox.warning( |
1133 self, |
1181 self, |
1134 self.tr("Run Script"), |
1182 self.tr("Run Script"), |
1135 self.tr("""<p>Cannot run script.</p><p>Reason:""" |
1183 self.tr( |
1136 """ {0}</p>""").format(reason)) |
1184 """<p>Cannot run script.</p><p>Reason:""" """ {0}</p>""" |
|
1185 ).format(reason), |
|
1186 ) |
1137 return |
1187 return |
1138 |
1188 |
1139 if not self.replButton.isChecked(): |
1189 if not self.replButton.isChecked(): |
1140 # activate on the REPL |
1190 # activate on the REPL |
1141 self.on_replButton_clicked(True) |
1191 self.on_replButton_clicked(True) |
1142 if self.replButton.isChecked(): |
1192 if self.replButton.isChecked(): |
1143 self.__device.runScript(script) |
1193 self.__device.runScript(script) |
1144 |
1194 |
1145 @pyqtSlot() |
1195 @pyqtSlot() |
1146 def on_openButton_clicked(self): |
1196 def on_openButton_clicked(self): |
1147 """ |
1197 """ |
1148 Private slot to open a file of the connected device. |
1198 Private slot to open a file of the connected device. |
1149 """ |
1199 """ |
1150 if not self.__device: |
1200 if not self.__device: |
1151 self.__showNoDeviceMessage() |
1201 self.__showNoDeviceMessage() |
1152 return |
1202 return |
1153 |
1203 |
1154 workspace = self.getDeviceWorkspace() |
1204 workspace = self.getDeviceWorkspace() |
1155 if workspace: |
1205 if workspace: |
1156 fileName = EricFileDialog.getOpenFileName( |
1206 fileName = EricFileDialog.getOpenFileName( |
1157 self, |
1207 self, |
1158 self.tr("Open Python File"), |
1208 self.tr("Open Python File"), |
1159 workspace, |
1209 workspace, |
1160 self.tr("Python3 Files (*.py);;All Files (*)")) |
1210 self.tr("Python3 Files (*.py);;All Files (*)"), |
|
1211 ) |
1161 if fileName: |
1212 if fileName: |
1162 ericApp().getObject("ViewManager").openSourceFile(fileName) |
1213 ericApp().getObject("ViewManager").openSourceFile(fileName) |
1163 |
1214 |
1164 @pyqtSlot() |
1215 @pyqtSlot() |
1165 def on_saveButton_clicked(self): |
1216 def on_saveButton_clicked(self): |
1166 """ |
1217 """ |
1167 Private slot to save the current editor to the connected device. |
1218 Private slot to save the current editor to the connected device. |
1168 """ |
1219 """ |
1169 if not self.__device: |
1220 if not self.__device: |
1170 self.__showNoDeviceMessage() |
1221 self.__showNoDeviceMessage() |
1171 return |
1222 return |
1172 |
1223 |
1173 aw = ericApp().getObject("ViewManager").activeWindow() |
1224 aw = ericApp().getObject("ViewManager").activeWindow() |
1174 if aw: |
1225 if aw: |
1175 workspace = self.getDeviceWorkspace() |
1226 workspace = self.getDeviceWorkspace() |
1176 if workspace: |
1227 if workspace: |
1177 aw.saveFileAs(workspace) |
1228 aw.saveFileAs(workspace) |
1178 |
1229 |
1179 @pyqtSlot(bool) |
1230 @pyqtSlot(bool) |
1180 def on_chartButton_clicked(self, checked): |
1231 def on_chartButton_clicked(self, checked): |
1181 """ |
1232 """ |
1182 Private slot to open a chart view to plot data received from the |
1233 Private slot to open a chart view to plot data received from the |
1183 connected device. |
1234 connected device. |
1184 |
1235 |
1185 If the selected device is not connected yet, this will be done now. |
1236 If the selected device is not connected yet, this will be done now. |
1186 |
1237 |
1187 @param checked state of the button |
1238 @param checked state of the button |
1188 @type bool |
1239 @type bool |
1189 """ |
1240 """ |
1190 if not HAS_QTCHART: |
1241 if not HAS_QTCHART: |
1191 # QtCharts not available => fail silently |
1242 # QtCharts not available => fail silently |
1192 return |
1243 return |
1193 |
1244 |
1194 if not self.__device: |
1245 if not self.__device: |
1195 self.__showNoDeviceMessage() |
1246 self.__showNoDeviceMessage() |
1196 return |
1247 return |
1197 |
1248 |
1198 if checked: |
1249 if checked: |
1199 ok, reason = self.__device.canStartPlotter() |
1250 ok, reason = self.__device.canStartPlotter() |
1200 if not ok: |
1251 if not ok: |
1201 EricMessageBox.warning( |
1252 EricMessageBox.warning( |
1202 self, |
1253 self, |
1203 self.tr("Start Chart"), |
1254 self.tr("Start Chart"), |
1204 self.tr("""<p>The Chart cannot be started.</p><p>Reason:""" |
1255 self.tr( |
1205 """ {0}</p>""").format(reason)) |
1256 """<p>The Chart cannot be started.</p><p>Reason:""" |
|
1257 """ {0}</p>""" |
|
1258 ).format(reason), |
|
1259 ) |
1206 return |
1260 return |
1207 |
1261 |
1208 self.__chartWidget = MicroPythonGraphWidget(self) |
1262 self.__chartWidget = MicroPythonGraphWidget(self) |
1209 self.__interface.dataReceived.connect( |
1263 self.__interface.dataReceived.connect(self.__chartWidget.processData) |
1210 self.__chartWidget.processData) |
1264 self.__chartWidget.dataFlood.connect(self.handleDataFlood) |
1211 self.__chartWidget.dataFlood.connect( |
1265 |
1212 self.handleDataFlood) |
1266 self.__ui.addSideWidget( |
1213 |
1267 self.__ui.BottomSide, |
1214 self.__ui.addSideWidget(self.__ui.BottomSide, self.__chartWidget, |
1268 self.__chartWidget, |
1215 UI.PixmapCache.getIcon("chart"), |
1269 UI.PixmapCache.getIcon("chart"), |
1216 self.tr("µPy Chart")) |
1270 self.tr("µPy Chart"), |
|
1271 ) |
1217 self.__ui.showSideWidget(self.__chartWidget) |
1272 self.__ui.showSideWidget(self.__chartWidget) |
1218 |
1273 |
1219 if not self.__interface.isConnected(): |
1274 if not self.__interface.isConnected(): |
1220 self.__connectToDevice() |
1275 self.__connectToDevice() |
1221 if self.__device.forceInterrupt(): |
1276 if self.__device.forceInterrupt(): |
1222 # send a Ctrl-B (exit raw mode) |
1277 # send a Ctrl-B (exit raw mode) |
1223 self.__interface.write(b'\x02') |
1278 self.__interface.write(b"\x02") |
1224 # send Ctrl-C (keyboard interrupt) |
1279 # send Ctrl-C (keyboard interrupt) |
1225 self.__interface.write(b'\x03') |
1280 self.__interface.write(b"\x03") |
1226 |
1281 |
1227 self.__device.setPlotter(True) |
1282 self.__device.setPlotter(True) |
1228 else: |
1283 else: |
1229 if self.__chartWidget.isDirty(): |
1284 if self.__chartWidget.isDirty(): |
1230 res = EricMessageBox.okToClearData( |
1285 res = EricMessageBox.okToClearData( |
1231 self, |
1286 self, |
1232 self.tr("Unsaved Chart Data"), |
1287 self.tr("Unsaved Chart Data"), |
1233 self.tr("""The chart contains unsaved data."""), |
1288 self.tr("""The chart contains unsaved data."""), |
1234 self.__chartWidget.saveData) |
1289 self.__chartWidget.saveData, |
|
1290 ) |
1235 if not res: |
1291 if not res: |
1236 # abort |
1292 # abort |
1237 return |
1293 return |
1238 |
1294 |
1239 self.__interface.dataReceived.disconnect( |
1295 self.__interface.dataReceived.disconnect(self.__chartWidget.processData) |
1240 self.__chartWidget.processData) |
1296 self.__chartWidget.dataFlood.disconnect(self.handleDataFlood) |
1241 self.__chartWidget.dataFlood.disconnect( |
1297 |
1242 self.handleDataFlood) |
1298 if not self.replButton.isChecked() and not self.filesButton.isChecked(): |
1243 |
|
1244 if (not self.replButton.isChecked() and |
|
1245 not self.filesButton.isChecked()): |
|
1246 self.__disconnectFromDevice() |
1299 self.__disconnectFromDevice() |
1247 |
1300 |
1248 self.__device.setPlotter(False) |
1301 self.__device.setPlotter(False) |
1249 self.__ui.removeSideWidget(self.__chartWidget) |
1302 self.__ui.removeSideWidget(self.__chartWidget) |
1250 |
1303 |
1251 self.__chartWidget.deleteLater() |
1304 self.__chartWidget.deleteLater() |
1252 self.__chartWidget = None |
1305 self.__chartWidget = None |
1253 |
1306 |
1254 self.chartButton.setChecked(checked) |
1307 self.chartButton.setChecked(checked) |
1255 |
1308 |
1256 @pyqtSlot() |
1309 @pyqtSlot() |
1257 def handleDataFlood(self): |
1310 def handleDataFlood(self): |
1258 """ |
1311 """ |
1259 Public slot handling a data flood from the device. |
1312 Public slot handling a data flood from the device. |
1260 """ |
1313 """ |
1261 self.on_connectButton_clicked() |
1314 self.on_connectButton_clicked() |
1262 self.__device.handleDataFlood() |
1315 self.__device.handleDataFlood() |
1263 |
1316 |
1264 @pyqtSlot(bool) |
1317 @pyqtSlot(bool) |
1265 def on_filesButton_clicked(self, checked): |
1318 def on_filesButton_clicked(self, checked): |
1266 """ |
1319 """ |
1267 Private slot to open a file manager window to the connected device. |
1320 Private slot to open a file manager window to the connected device. |
1268 |
1321 |
1269 If the selected device is not connected yet, this will be done now. |
1322 If the selected device is not connected yet, this will be done now. |
1270 |
1323 |
1271 @param checked state of the button |
1324 @param checked state of the button |
1272 @type bool |
1325 @type bool |
1273 """ |
1326 """ |
1274 if not self.__device: |
1327 if not self.__device: |
1275 self.__showNoDeviceMessage() |
1328 self.__showNoDeviceMessage() |
1276 return |
1329 return |
1277 |
1330 |
1278 if checked: |
1331 if checked: |
1279 ok, reason = self.__device.canStartFileManager() |
1332 ok, reason = self.__device.canStartFileManager() |
1280 if not ok: |
1333 if not ok: |
1281 EricMessageBox.warning( |
1334 EricMessageBox.warning( |
1282 self, |
1335 self, |
1283 self.tr("Start File Manager"), |
1336 self.tr("Start File Manager"), |
1284 self.tr("""<p>The File Manager cannot be started.</p>""" |
1337 self.tr( |
1285 """<p>Reason: {0}</p>""").format(reason)) |
1338 """<p>The File Manager cannot be started.</p>""" |
|
1339 """<p>Reason: {0}</p>""" |
|
1340 ).format(reason), |
|
1341 ) |
1286 return |
1342 return |
1287 |
1343 |
1288 with EricOverrideCursor(): |
1344 with EricOverrideCursor(): |
1289 if not self.__interface.isConnected(): |
1345 if not self.__interface.isConnected(): |
1290 self.__connectToDevice() |
1346 self.__connectToDevice() |
1291 if self.__connected: |
1347 if self.__connected: |
1292 self.__fileManagerWidget = MicroPythonFileManagerWidget( |
1348 self.__fileManagerWidget = MicroPythonFileManagerWidget( |
1293 self.__interface, |
1349 self.__interface, self.__device.supportsLocalFileAccess(), self |
1294 self.__device.supportsLocalFileAccess(), |
1350 ) |
1295 self) |
1351 |
1296 |
|
1297 self.__ui.addSideWidget( |
1352 self.__ui.addSideWidget( |
1298 self.__ui.BottomSide, |
1353 self.__ui.BottomSide, |
1299 self.__fileManagerWidget, |
1354 self.__fileManagerWidget, |
1300 UI.PixmapCache.getIcon("filemanager"), |
1355 UI.PixmapCache.getIcon("filemanager"), |
1301 self.tr("µPy Files") |
1356 self.tr("µPy Files"), |
1302 ) |
1357 ) |
1303 self.__ui.showSideWidget(self.__fileManagerWidget) |
1358 self.__ui.showSideWidget(self.__fileManagerWidget) |
1304 |
1359 |
1305 self.__device.setFileManager(True) |
1360 self.__device.setFileManager(True) |
1306 |
1361 |
1307 self.__fileManagerWidget.start() |
1362 self.__fileManagerWidget.start() |
1308 else: |
1363 else: |
1309 self.__fileManagerWidget.stop() |
1364 self.__fileManagerWidget.stop() |
1310 |
1365 |
1311 if (not self.replButton.isChecked() and |
1366 if not self.replButton.isChecked() and not self.chartButton.isChecked(): |
1312 not self.chartButton.isChecked()): |
|
1313 self.__disconnectFromDevice() |
1367 self.__disconnectFromDevice() |
1314 |
1368 |
1315 self.__device.setFileManager(False) |
1369 self.__device.setFileManager(False) |
1316 self.__ui.removeSideWidget(self.__fileManagerWidget) |
1370 self.__ui.removeSideWidget(self.__fileManagerWidget) |
1317 |
1371 |
1318 self.__fileManagerWidget.deleteLater() |
1372 self.__fileManagerWidget.deleteLater() |
1319 self.__fileManagerWidget = None |
1373 self.__fileManagerWidget = None |
1320 |
1374 |
1321 self.filesButton.setChecked(checked) |
1375 self.filesButton.setChecked(checked) |
1322 |
1376 |
1323 ################################################################## |
1377 ################################################################## |
1324 ## Super Menu related methods below |
1378 ## Super Menu related methods below |
1325 ################################################################## |
1379 ################################################################## |
1326 |
1380 |
1327 def __aboutToShowSuperMenu(self): |
1381 def __aboutToShowSuperMenu(self): |
1328 """ |
1382 """ |
1329 Private slot to populate the Super Menu before showing it. |
1383 Private slot to populate the Super Menu before showing it. |
1330 """ |
1384 """ |
1331 self.__superMenu.clear() |
1385 self.__superMenu.clear() |
1332 |
1386 |
1333 # prepare the download menu |
1387 # prepare the download menu |
1334 if self.__device: |
1388 if self.__device: |
1335 menuEntries = self.__device.getDownloadMenuEntries() |
1389 menuEntries = self.__device.getDownloadMenuEntries() |
1336 if menuEntries: |
1390 if menuEntries: |
1337 downloadMenu = QMenu(self.tr("Downloads"), self.__superMenu) |
1391 downloadMenu = QMenu(self.tr("Downloads"), self.__superMenu) |
1338 for text, url in menuEntries: |
1392 for text, url in menuEntries: |
1339 if text == "<separator>": |
1393 if text == "<separator>": |
1340 downloadMenu.addSeparator() |
1394 downloadMenu.addSeparator() |
1341 else: |
1395 else: |
1342 downloadMenu.addAction( |
1396 downloadMenu.addAction( |
1343 text, |
1397 text, functools.partial(self.__downloadFromUrl, url) |
1344 functools.partial(self.__downloadFromUrl, url) |
|
1345 ) |
1398 ) |
1346 else: |
1399 else: |
1347 downloadMenu = None |
1400 downloadMenu = None |
1348 |
1401 |
1349 # populate the super menu |
1402 # populate the super menu |
1350 hasTime = self.__device.hasTimeCommands() if self.__device else False |
1403 hasTime = self.__device.hasTimeCommands() if self.__device else False |
1351 |
1404 |
1352 act = self.__superMenu.addAction( |
1405 act = self.__superMenu.addAction( |
1353 self.tr("Show Version"), self.__showDeviceVersion) |
1406 self.tr("Show Version"), self.__showDeviceVersion |
|
1407 ) |
1354 act.setEnabled(self.__connected) |
1408 act.setEnabled(self.__connected) |
1355 act = self.__superMenu.addAction( |
1409 act = self.__superMenu.addAction( |
1356 self.tr("Show Implementation"), self.__showImplementation) |
1410 self.tr("Show Implementation"), self.__showImplementation |
|
1411 ) |
1357 act.setEnabled(self.__connected) |
1412 act.setEnabled(self.__connected) |
1358 act = self.__superMenu.addAction( |
1413 act = self.__superMenu.addAction( |
1359 self.tr("Show Board Data"), self.__showBoardInformation) |
1414 self.tr("Show Board Data"), self.__showBoardInformation |
|
1415 ) |
1360 act.setEnabled(self.__connected) |
1416 act.setEnabled(self.__connected) |
1361 self.__superMenu.addSeparator() |
1417 self.__superMenu.addSeparator() |
1362 if hasTime: |
1418 if hasTime: |
1363 act = self.__superMenu.addAction( |
1419 act = self.__superMenu.addAction( |
1364 self.tr("Synchronize Time"), self.__synchronizeTime) |
1420 self.tr("Synchronize Time"), self.__synchronizeTime |
|
1421 ) |
1365 act.setEnabled(self.__connected) |
1422 act.setEnabled(self.__connected) |
1366 act = self.__superMenu.addAction( |
1423 act = self.__superMenu.addAction( |
1367 self.tr("Show Device Time"), self.__showDeviceTime) |
1424 self.tr("Show Device Time"), self.__showDeviceTime |
|
1425 ) |
1368 act.setEnabled(self.__connected) |
1426 act.setEnabled(self.__connected) |
1369 self.__superMenu.addAction( |
1427 self.__superMenu.addAction(self.tr("Show Local Time"), self.__showLocalTime) |
1370 self.tr("Show Local Time"), self.__showLocalTime) |
|
1371 if hasTime: |
1428 if hasTime: |
1372 act = self.__superMenu.addAction( |
1429 act = self.__superMenu.addAction( |
1373 self.tr("Show Time"), self.__showLocalAndDeviceTime) |
1430 self.tr("Show Time"), self.__showLocalAndDeviceTime |
|
1431 ) |
1374 act.setEnabled(self.__connected) |
1432 act.setEnabled(self.__connected) |
1375 self.__superMenu.addSeparator() |
1433 self.__superMenu.addSeparator() |
1376 if not Globals.isWindowsPlatform(): |
1434 if not Globals.isWindowsPlatform(): |
1377 available = self.__mpyCrossAvailable() |
1435 available = self.__mpyCrossAvailable() |
1378 act = self.__superMenu.addAction( |
1436 act = self.__superMenu.addAction( |
1379 self.tr("Compile Python File"), self.__compileFile2Mpy) |
1437 self.tr("Compile Python File"), self.__compileFile2Mpy |
|
1438 ) |
1380 act.setEnabled(available) |
1439 act.setEnabled(available) |
1381 act = self.__superMenu.addAction( |
1440 act = self.__superMenu.addAction( |
1382 self.tr("Compile Current Editor"), self.__compileEditor2Mpy) |
1441 self.tr("Compile Current Editor"), self.__compileEditor2Mpy |
|
1442 ) |
1383 aw = ericApp().getObject("ViewManager").activeWindow() |
1443 aw = ericApp().getObject("ViewManager").activeWindow() |
1384 act.setEnabled(available and bool(aw)) |
1444 act.setEnabled(available and bool(aw)) |
1385 self.__superMenu.addSeparator() |
1445 self.__superMenu.addSeparator() |
1386 if self.__device: |
1446 if self.__device: |
1387 self.__device.addDeviceMenuEntries(self.__superMenu) |
1447 self.__device.addDeviceMenuEntries(self.__superMenu) |
1388 self.__superMenu.addSeparator() |
1448 self.__superMenu.addSeparator() |
1389 if downloadMenu is None: |
1449 if downloadMenu is None: |
1390 # generic download action |
1450 # generic download action |
1391 act = self.__superMenu.addAction( |
1451 act = self.__superMenu.addAction( |
1392 self.tr("Download Firmware"), self.__downloadFirmware) |
1452 self.tr("Download Firmware"), self.__downloadFirmware |
|
1453 ) |
1393 act.setEnabled(self.__device.hasFirmwareUrl()) |
1454 act.setEnabled(self.__device.hasFirmwareUrl()) |
1394 else: |
1455 else: |
1395 # download sub-menu |
1456 # download sub-menu |
1396 self.__superMenu.addMenu(downloadMenu) |
1457 self.__superMenu.addMenu(downloadMenu) |
1397 self.__superMenu.addSeparator() |
1458 self.__superMenu.addSeparator() |
1398 act = self.__superMenu.addAction( |
1459 act = self.__superMenu.addAction( |
1399 self.tr("Show Documentation"), self.__showDocumentation) |
1460 self.tr("Show Documentation"), self.__showDocumentation |
|
1461 ) |
1400 act.setEnabled(self.__device.hasDocumentationUrl()) |
1462 act.setEnabled(self.__device.hasDocumentationUrl()) |
1401 self.__superMenu.addSeparator() |
1463 self.__superMenu.addSeparator() |
1402 if not self.__device.hasFlashMenuEntry(): |
1464 if not self.__device.hasFlashMenuEntry(): |
1403 self.__superMenu.addAction(self.tr("Flash UF2 Device"), |
1465 self.__superMenu.addAction(self.tr("Flash UF2 Device"), self.__flashUF2) |
1404 self.__flashUF2) |
|
1405 self.__superMenu.addSeparator() |
1466 self.__superMenu.addSeparator() |
1406 self.__superMenu.addAction(self.tr("Manage Unknown Devices"), |
1467 self.__superMenu.addAction( |
1407 self.__manageUnknownDevices) |
1468 self.tr("Manage Unknown Devices"), self.__manageUnknownDevices |
1408 self.__superMenu.addAction(self.tr("Ignored Serial Devices"), |
1469 ) |
1409 self.__manageIgnored) |
1470 self.__superMenu.addAction( |
|
1471 self.tr("Ignored Serial Devices"), self.__manageIgnored |
|
1472 ) |
1410 self.__superMenu.addSeparator() |
1473 self.__superMenu.addSeparator() |
1411 self.__superMenu.addAction(self.tr("Configure"), self.__configure) |
1474 self.__superMenu.addAction(self.tr("Configure"), self.__configure) |
1412 |
1475 |
1413 @pyqtSlot() |
1476 @pyqtSlot() |
1414 def __showDeviceVersion(self): |
1477 def __showDeviceVersion(self): |
1415 """ |
1478 """ |
1416 Private slot to show some version info about MicroPython of the device. |
1479 Private slot to show some version info about MicroPython of the device. |
1417 """ |
1480 """ |
1418 try: |
1481 try: |
1419 versionInfo = self.__interface.version() |
1482 versionInfo = self.__interface.version() |
1420 if versionInfo: |
1483 if versionInfo: |
1421 msg = self.tr( |
1484 msg = self.tr("<h3>Device Version Information</h3>") |
1422 "<h3>Device Version Information</h3>" |
|
1423 ) |
|
1424 msg += "<table>" |
1485 msg += "<table>" |
1425 for key, value in versionInfo.items(): |
1486 for key, value in versionInfo.items(): |
1426 msg += "<tr><td><b>{0}</b></td><td>{1}</td></tr>".format( |
1487 msg += "<tr><td><b>{0}</b></td><td>{1}</td></tr>".format( |
1427 key.capitalize(), value) |
1488 key.capitalize(), value |
|
1489 ) |
1428 msg += "</table>" |
1490 msg += "</table>" |
1429 else: |
1491 else: |
1430 msg = self.tr("No version information available.") |
1492 msg = self.tr("No version information available.") |
1431 |
1493 |
1432 EricMessageBox.information( |
1494 EricMessageBox.information(self, self.tr("Device Version Information"), msg) |
1433 self, |
|
1434 self.tr("Device Version Information"), |
|
1435 msg) |
|
1436 except Exception as exc: |
1495 except Exception as exc: |
1437 self.__showError("version()", str(exc)) |
1496 self.__showError("version()", str(exc)) |
1438 |
1497 |
1439 @pyqtSlot() |
1498 @pyqtSlot() |
1440 def __showImplementation(self): |
1499 def __showImplementation(self): |
1441 """ |
1500 """ |
1442 Private slot to show some implementation related information. |
1501 Private slot to show some implementation related information. |
1443 """ |
1502 """ |
1526 "<tr><td><b>Date</b></td><td>{0}</td></tr>" |
1588 "<tr><td><b>Date</b></td><td>{0}</td></tr>" |
1527 "<tr><td><b>Time</b></td><td>{1}</td></tr>" |
1589 "<tr><td><b>Time</b></td><td>{1}</td></tr>" |
1528 "</table>" |
1590 "</table>" |
1529 ).format(date, time) |
1591 ).format(date, time) |
1530 except ValueError: |
1592 except ValueError: |
1531 return self.tr( |
1593 return self.tr("<h3>Device Date and Time</h3>" "<p>{0}</p>").format( |
1532 "<h3>Device Date and Time</h3>" |
1594 dateTimeString.strip() |
1533 "<p>{0}</p>" |
1595 ) |
1534 ).format(dateTimeString.strip()) |
|
1535 except Exception as exc: |
1596 except Exception as exc: |
1536 self.__showError("getTime()", str(exc)) |
1597 self.__showError("getTime()", str(exc)) |
1537 return "" |
1598 return "" |
1538 else: |
1599 else: |
1539 return "" |
1600 return "" |
1540 |
1601 |
1541 @pyqtSlot() |
1602 @pyqtSlot() |
1542 def __showDeviceTime(self): |
1603 def __showDeviceTime(self): |
1543 """ |
1604 """ |
1544 Private slot to show the date and time of the connected device. |
1605 Private slot to show the date and time of the connected device. |
1545 """ |
1606 """ |
1546 msg = self.__getDeviceTime() |
1607 msg = self.__getDeviceTime() |
1547 if msg: |
1608 if msg: |
1548 EricMessageBox.information( |
1609 EricMessageBox.information(self, self.tr("Device Date and Time"), msg) |
1549 self, |
1610 |
1550 self.tr("Device Date and Time"), |
|
1551 msg) |
|
1552 |
|
1553 @pyqtSlot() |
1611 @pyqtSlot() |
1554 def __showLocalTime(self): |
1612 def __showLocalTime(self): |
1555 """ |
1613 """ |
1556 Private slot to show the local date and time. |
1614 Private slot to show the local date and time. |
1557 """ |
1615 """ |
1558 localdatetime = time.localtime() |
1616 localdatetime = time.localtime() |
1559 localdate = time.strftime('%Y-%m-%d', localdatetime) |
1617 localdate = time.strftime("%Y-%m-%d", localdatetime) |
1560 localtime = time.strftime('%H:%M:%S', localdatetime) |
1618 localtime = time.strftime("%H:%M:%S", localdatetime) |
1561 EricMessageBox.information( |
1619 EricMessageBox.information( |
1562 self, |
1620 self, |
1563 self.tr("Local Date and Time"), |
1621 self.tr("Local Date and Time"), |
1564 self.tr("<h3>Local Date and Time</h3>" |
1622 self.tr( |
1565 "<table>" |
1623 "<h3>Local Date and Time</h3>" |
1566 "<tr><td><b>Date</b></td><td>{0}</td></tr>" |
1624 "<table>" |
1567 "<tr><td><b>Time</b></td><td>{1}</td></tr>" |
1625 "<tr><td><b>Date</b></td><td>{0}</td></tr>" |
1568 "</table>" |
1626 "<tr><td><b>Time</b></td><td>{1}</td></tr>" |
1569 ).format(localdate, localtime) |
1627 "</table>" |
|
1628 ).format(localdate, localtime), |
1570 ) |
1629 ) |
1571 |
1630 |
1572 @pyqtSlot() |
1631 @pyqtSlot() |
1573 def __showLocalAndDeviceTime(self): |
1632 def __showLocalAndDeviceTime(self): |
1574 """ |
1633 """ |
1575 Private slot to show the local and device time side-by-side. |
1634 Private slot to show the local and device time side-by-side. |
1576 """ |
1635 """ |
1577 localdatetime = time.localtime() |
1636 localdatetime = time.localtime() |
1578 localdate = time.strftime('%Y-%m-%d', localdatetime) |
1637 localdate = time.strftime("%Y-%m-%d", localdatetime) |
1579 localtime = time.strftime('%H:%M:%S', localdatetime) |
1638 localtime = time.strftime("%H:%M:%S", localdatetime) |
1580 |
1639 |
1581 try: |
1640 try: |
1582 deviceDateTimeString = self.__interface.getTime() |
1641 deviceDateTimeString = self.__interface.getTime() |
1583 try: |
1642 try: |
1584 devicedate, devicetime = ( |
1643 devicedate, devicetime = deviceDateTimeString.strip().split(None, 1) |
1585 deviceDateTimeString.strip().split(None, 1) |
|
1586 ) |
|
1587 EricMessageBox.information( |
1644 EricMessageBox.information( |
1588 self, |
1645 self, |
1589 self.tr("Date and Time"), |
1646 self.tr("Date and Time"), |
1590 self.tr("<table>" |
1647 self.tr( |
1591 "<tr><th></th><th>Local Date and Time</th>" |
1648 "<table>" |
1592 "<th>Device Date and Time</th></tr>" |
1649 "<tr><th></th><th>Local Date and Time</th>" |
1593 "<tr><td><b>Date</b></td>" |
1650 "<th>Device Date and Time</th></tr>" |
1594 "<td align='center'>{0}</td>" |
1651 "<tr><td><b>Date</b></td>" |
1595 "<td align='center'>{2}</td></tr>" |
1652 "<td align='center'>{0}</td>" |
1596 "<tr><td><b>Time</b></td>" |
1653 "<td align='center'>{2}</td></tr>" |
1597 "<td align='center'>{1}</td>" |
1654 "<tr><td><b>Time</b></td>" |
1598 "<td align='center'>{3}</td></tr>" |
1655 "<td align='center'>{1}</td>" |
1599 "</table>" |
1656 "<td align='center'>{3}</td></tr>" |
1600 ).format(localdate, localtime, |
1657 "</table>" |
1601 devicedate, devicetime) |
1658 ).format(localdate, localtime, devicedate, devicetime), |
1602 ) |
1659 ) |
1603 except ValueError: |
1660 except ValueError: |
1604 EricMessageBox.information( |
1661 EricMessageBox.information( |
1605 self, |
1662 self, |
1606 self.tr("Date and Time"), |
1663 self.tr("Date and Time"), |
1607 self.tr("<table>" |
1664 self.tr( |
1608 "<tr><th>Local Date and Time</th>" |
1665 "<table>" |
1609 "<th>Device Date and Time</th></tr>" |
1666 "<tr><th>Local Date and Time</th>" |
1610 "<tr><td align='center'>{0} {1}</td>" |
1667 "<th>Device Date and Time</th></tr>" |
1611 "<td align='center'>{2}</td></tr>" |
1668 "<tr><td align='center'>{0} {1}</td>" |
1612 "</table>" |
1669 "<td align='center'>{2}</td></tr>" |
1613 ).format(localdate, localtime, |
1670 "</table>" |
1614 deviceDateTimeString.strip()) |
1671 ).format(localdate, localtime, deviceDateTimeString.strip()), |
1615 ) |
1672 ) |
1616 except Exception as exc: |
1673 except Exception as exc: |
1617 self.__showError("getTime()", str(exc)) |
1674 self.__showError("getTime()", str(exc)) |
1618 |
1675 |
1619 def __showError(self, method, error): |
1676 def __showError(self, method, error): |
1620 """ |
1677 """ |
1621 Private method to show some error message. |
1678 Private method to show some error message. |
1622 |
1679 |
1623 @param method name of the method the error occured in |
1680 @param method name of the method the error occured in |
1624 @type str |
1681 @type str |
1625 @param error error message |
1682 @param error error message |
1626 @type str |
1683 @type str |
1627 """ |
1684 """ |
1628 with EricOverridenCursor(): |
1685 with EricOverridenCursor(): |
1629 EricMessageBox.warning( |
1686 EricMessageBox.warning( |
1630 self, |
1687 self, |
1631 self.tr("Error handling device"), |
1688 self.tr("Error handling device"), |
1632 self.tr("<p>There was an error communicating with the" |
1689 self.tr( |
1633 " connected device.</p><p>Method: {0}</p>" |
1690 "<p>There was an error communicating with the" |
1634 "<p>Message: {1}</p>") |
1691 " connected device.</p><p>Method: {0}</p>" |
1635 .format(method, error)) |
1692 "<p>Message: {1}</p>" |
1636 |
1693 ).format(method, error), |
|
1694 ) |
|
1695 |
1637 def __mpyCrossAvailable(self): |
1696 def __mpyCrossAvailable(self): |
1638 """ |
1697 """ |
1639 Private method to check the availability of mpy-cross. |
1698 Private method to check the availability of mpy-cross. |
1640 |
1699 |
1641 @return flag indicating the availability of mpy-cross |
1700 @return flag indicating the availability of mpy-cross |
1642 @rtype bool |
1701 @rtype bool |
1643 """ |
1702 """ |
1644 available = False |
1703 available = False |
1645 program = Preferences.getMicroPython("MpyCrossCompiler") |
1704 program = Preferences.getMicroPython("MpyCrossCompiler") |
1732 if not aw.isPyFile(): |
1797 if not aw.isPyFile(): |
1733 # no Python file |
1798 # no Python file |
1734 EricMessageBox.critical( |
1799 EricMessageBox.critical( |
1735 self, |
1800 self, |
1736 self.tr("Compile Current Editor"), |
1801 self.tr("Compile Current Editor"), |
1737 self.tr("""The current editor does not contain a Python""" |
1802 self.tr( |
1738 """ file. Aborting...""")) |
1803 """The current editor does not contain a Python""" |
|
1804 """ file. Aborting...""" |
|
1805 ), |
|
1806 ) |
1739 return |
1807 return |
1740 |
1808 |
1741 self.__crossCompile( |
1809 self.__crossCompile( |
1742 pythonFile=aw.getFileName(), |
1810 pythonFile=aw.getFileName(), title=self.tr("Compile Current Editor") |
1743 title=self.tr("Compile Current Editor") |
|
1744 ) |
1811 ) |
1745 |
1812 |
1746 @pyqtSlot() |
1813 @pyqtSlot() |
1747 def __showDocumentation(self): |
1814 def __showDocumentation(self): |
1748 """ |
1815 """ |
1749 Private slot to open the documentation URL for the selected device. |
1816 Private slot to open the documentation URL for the selected device. |
1750 """ |
1817 """ |
1751 if self.__device is None or not self.__device.hasDocumentationUrl(): |
1818 if self.__device is None or not self.__device.hasDocumentationUrl(): |
1752 # abort silently |
1819 # abort silently |
1753 return |
1820 return |
1754 |
1821 |
1755 url = self.__device.getDocumentationUrl() |
1822 url = self.__device.getDocumentationUrl() |
1756 ericApp().getObject("UserInterface").launchHelpViewer(url) |
1823 ericApp().getObject("UserInterface").launchHelpViewer(url) |
1757 |
1824 |
1758 @pyqtSlot() |
1825 @pyqtSlot() |
1759 def __downloadFirmware(self): |
1826 def __downloadFirmware(self): |
1760 """ |
1827 """ |
1761 Private slot to open the firmware download page. |
1828 Private slot to open the firmware download page. |
1762 """ |
1829 """ |
1763 if self.__device is None or not self.__device.hasFirmwareUrl(): |
1830 if self.__device is None or not self.__device.hasFirmwareUrl(): |
1764 # abort silently |
1831 # abort silently |
1765 return |
1832 return |
1766 |
1833 |
1767 self.__device.downloadFirmware() |
1834 self.__device.downloadFirmware() |
1768 |
1835 |
1769 def __downloadFromUrl(self, url): |
1836 def __downloadFromUrl(self, url): |
1770 """ |
1837 """ |
1771 Private method to open a web browser for the given URL. |
1838 Private method to open a web browser for the given URL. |
1772 |
1839 |
1773 @param url URL to be opened |
1840 @param url URL to be opened |
1774 @type str |
1841 @type str |
1775 """ |
1842 """ |
1776 if self.__device is None: |
1843 if self.__device is None: |
1777 # abort silently |
1844 # abort silently |
1778 return |
1845 return |
1779 |
1846 |
1780 if url: |
1847 if url: |
1781 ericApp().getObject("UserInterface").launchHelpViewer(url) |
1848 ericApp().getObject("UserInterface").launchHelpViewer(url) |
1782 |
1849 |
1783 @pyqtSlot() |
1850 @pyqtSlot() |
1784 def __manageIgnored(self): |
1851 def __manageIgnored(self): |
1785 """ |
1852 """ |
1786 Private slot to manage the list of ignored serial devices. |
1853 Private slot to manage the list of ignored serial devices. |
1787 """ |
1854 """ |
1788 from .IgnoredDevicesDialog import IgnoredDevicesDialog |
1855 from .IgnoredDevicesDialog import IgnoredDevicesDialog |
1789 |
1856 |
1790 dlg = IgnoredDevicesDialog( |
1857 dlg = IgnoredDevicesDialog( |
1791 Preferences.getMicroPython("IgnoredUnknownDevices"), |
1858 Preferences.getMicroPython("IgnoredUnknownDevices"), self |
1792 self) |
1859 ) |
1793 if dlg.exec() == QDialog.DialogCode.Accepted: |
1860 if dlg.exec() == QDialog.DialogCode.Accepted: |
1794 ignoredDevices = dlg.getDevices() |
1861 ignoredDevices = dlg.getDevices() |
1795 Preferences.setMicroPython("IgnoredUnknownDevices", |
1862 Preferences.setMicroPython("IgnoredUnknownDevices", ignoredDevices) |
1796 ignoredDevices) |
1863 |
1797 |
|
1798 @pyqtSlot() |
1864 @pyqtSlot() |
1799 def __configure(self): |
1865 def __configure(self): |
1800 """ |
1866 """ |
1801 Private slot to open the MicroPython configuration page. |
1867 Private slot to open the MicroPython configuration page. |
1802 """ |
1868 """ |
1803 ericApp().getObject("UserInterface").showPreferences("microPythonPage") |
1869 ericApp().getObject("UserInterface").showPreferences("microPythonPage") |
1804 |
1870 |
1805 @pyqtSlot() |
1871 @pyqtSlot() |
1806 def __manageUnknownDevices(self): |
1872 def __manageUnknownDevices(self): |
1807 """ |
1873 """ |
1808 Private slot to manage manually added boards (i.e. those not in the |
1874 Private slot to manage manually added boards (i.e. those not in the |
1809 list of supported boards). |
1875 list of supported boards). |
1810 """ |
1876 """ |
1811 from .UnknownDevicesDialog import UnknownDevicesDialog |
1877 from .UnknownDevicesDialog import UnknownDevicesDialog |
|
1878 |
1812 dlg = UnknownDevicesDialog() |
1879 dlg = UnknownDevicesDialog() |
1813 dlg.exec() |
1880 dlg.exec() |
1814 |
1881 |
1815 def __addUnknownDevices(self, devices): |
1882 def __addUnknownDevices(self, devices): |
1816 """ |
1883 """ |
1817 Private method to add devices to the list of manually added boards. |
1884 Private method to add devices to the list of manually added boards. |
1818 |
1885 |
1819 @param devices list of not ignored but unknown devices |
1886 @param devices list of not ignored but unknown devices |
1820 @type list of tuple of (int, int, str) |
1887 @type list of tuple of (int, int, str) |
1821 """ |
1888 """ |
1822 from .AddEditDevicesDialog import AddEditDevicesDialog |
1889 from .AddEditDevicesDialog import AddEditDevicesDialog |
1823 |
1890 |
1824 if len(devices) > 1: |
1891 if len(devices) > 1: |
1825 from EricWidgets.EricListSelectionDialog import ( |
1892 from EricWidgets.EricListSelectionDialog import EricListSelectionDialog |
1826 EricListSelectionDialog |
1893 |
1827 ) |
|
1828 sdlg = EricListSelectionDialog( |
1894 sdlg = EricListSelectionDialog( |
1829 [d[2] for d in devices], |
1895 [d[2] for d in devices], |
1830 title=self.tr("Add Unknown Devices"), |
1896 title=self.tr("Add Unknown Devices"), |
1831 message=self.tr("Select the devices to be added:"), |
1897 message=self.tr("Select the devices to be added:"), |
1832 checkBoxSelection=True |
1898 checkBoxSelection=True, |
1833 ) |
1899 ) |
1834 if sdlg.exec() == QDialog.DialogCode.Accepted: |
1900 if sdlg.exec() == QDialog.DialogCode.Accepted: |
1835 selectedDevices = sdlg.getSelection() |
1901 selectedDevices = sdlg.getSelection() |
1836 else: |
1902 else: |
1837 selectedDevices = devices[0][2] |
1903 selectedDevices = devices[0][2] |
1838 |
1904 |
1839 if selectedDevices: |
1905 if selectedDevices: |
1840 manualDevices = Preferences.getMicroPython("ManualDevices") |
1906 manualDevices = Preferences.getMicroPython("ManualDevices") |
1841 for vid, pid, description in devices: |
1907 for vid, pid, description in devices: |
1842 if description in selectedDevices: |
1908 if description in selectedDevices: |
1843 dlg = AddEditDevicesDialog(vid, pid, description) |
1909 dlg = AddEditDevicesDialog(vid, pid, description) |
1844 if dlg.exec() == QDialog.DialogCode.Accepted: |
1910 if dlg.exec() == QDialog.DialogCode.Accepted: |
1845 manualDevices.append(dlg.getDeviceDict()) |
1911 manualDevices.append(dlg.getDeviceDict()) |
1846 Preferences.setMicroPython("ManualDevices", manualDevices) |
1912 Preferences.setMicroPython("ManualDevices", manualDevices) |
1847 |
1913 |
1848 # rescan the ports |
1914 # rescan the ports |
1849 self.__populateDeviceTypeComboBox() |
1915 self.__populateDeviceTypeComboBox() |
1850 |
1916 |
1851 @pyqtSlot() |
1917 @pyqtSlot() |
1852 def __flashUF2(self): |
1918 def __flashUF2(self): |
1853 """ |
1919 """ |
1854 Private slot to flash MicroPython/CircuitPython to a device |
1920 Private slot to flash MicroPython/CircuitPython to a device |
1855 support the UF2 bootloader. |
1921 support the UF2 bootloader. |