eric6/MicroPython/MicroPythonReplWidget.py

branch
micropython
changeset 7065
e3d04faced34
parent 7062
ac12da95958b
child 7066
e3d034e65afc
equal deleted inserted replaced
7062:ac12da95958b 7065:e3d04faced34
20 try: 20 try:
21 from PyQt5.QtSerialPort import QSerialPort 21 from PyQt5.QtSerialPort import QSerialPort
22 HAS_QTSERIALPORT = True 22 HAS_QTSERIALPORT = True
23 except ImportError: 23 except ImportError:
24 HAS_QTSERIALPORT = False 24 HAS_QTSERIALPORT = False
25
26 from E5Gui.E5ZoomWidget import E5ZoomWidget
27 from E5Gui import E5MessageBox, E5FileDialog
28 from E5Gui.E5Application import e5App
29
30 from .Ui_MicroPythonReplWidget import Ui_MicroPythonReplWidget
31
32 from . import MicroPythonDevices
25 try: 33 try:
26 from PyQt5.QtChart import QChart # __IGNORE_WARNING__ 34 from .MicroPythonGraphWidget import MicroPythonGraphWidget
27 HAS_QTCHART = True 35 HAS_QTCHART = True
28 except ImportError: 36 except ImportError:
29 HAS_QTCHART = False 37 HAS_QTCHART = False
30
31 from E5Gui.E5ZoomWidget import E5ZoomWidget
32 from E5Gui import E5MessageBox, E5FileDialog
33 from E5Gui.E5Application import e5App
34
35 from .Ui_MicroPythonReplWidget import Ui_MicroPythonReplWidget
36
37 from . import MicroPythonDevices
38 38
39 import Globals 39 import Globals
40 import UI.PixmapCache 40 import UI.PixmapCache
41 41
42 42
95 self.__zoomWidget.setMinimum(self.ZoomMin) 95 self.__zoomWidget.setMinimum(self.ZoomMin)
96 self.__zoomWidget.setMaximum(self.ZoomMax) 96 self.__zoomWidget.setMaximum(self.ZoomMax)
97 self.__zoomWidget.valueChanged.connect(self.__doZoom) 97 self.__zoomWidget.valueChanged.connect(self.__doZoom)
98 self.__currentZoom = 0 98 self.__currentZoom = 0
99 99
100 self.__ui = None
101
100 self.__serial = None 102 self.__serial = None
101 self.__device = None 103 self.__device = None
102 self.setConnected(False) 104 self.setConnected(False)
103 105
104 self.__replRunning = False 106 self.__replRunning = False
180 182
181 def setActionButtons(self, **kwargs): 183 def setActionButtons(self, **kwargs):
182 """ 184 """
183 Public method to set the enabled state of the various action buttons. 185 Public method to set the enabled state of the various action buttons.
184 186
185 @param kwargs keyword arguments containg the enabled states (keys are 187 @keyparam kwargs keyword arguments containg the enabled states (keys
186 'run', 'repl', 'files', 'chart', 'open', 'save' 188 are 'run', 'repl', 'files', 'chart', 'open', 'save'
187 @type dict 189 @type dict
188 """ 190 """
189 if "open" in kwargs: 191 if "open" in kwargs:
190 self.openButton.setEnabled(kwargs["open"]) 192 self.openButton.setEnabled(kwargs["open"])
191 if "save" in kwargs: 193 if "save" in kwargs:
200 self.chartButton.setEnabled(kwargs["chart"]) 202 self.chartButton.setEnabled(kwargs["chart"])
201 203
202 @pyqtSlot(QPoint) 204 @pyqtSlot(QPoint)
203 def __showContextMenu(self, pos): 205 def __showContextMenu(self, pos):
204 """ 206 """
205 Privat slot to show the REPL context menu. 207 Private slot to show the REPL context menu.
206 208
207 @param pos position to show the menu at 209 @param pos position to show the menu at
208 @type QPoint 210 @type QPoint
209 """ 211 """
210 if Globals.isMacPlatform(): 212 if Globals.isMacPlatform():
257 self.__showNoDeviceMessage() 259 self.__showNoDeviceMessage()
258 return 260 return
259 261
260 if self.__replRunning: 262 if self.__replRunning:
261 self.dataReceived.disconnect(self.__processData) 263 self.dataReceived.disconnect(self.__processData)
262 self.__disconnect() 264 if not self.__plotterRunning:
265 self.__disconnectSerial()
263 self.__replRunning = False 266 self.__replRunning = False
264 self.__device.setRepl(False) 267 self.__device.setRepl(False)
265 else: 268 else:
266 ok, reason = self.__device.canStartRepl() 269 ok, reason = self.__device.canStartRepl()
267 if not ok: 270 if not ok:
269 self, 272 self,
270 self.tr("Start REPL"), 273 self.tr("Start REPL"),
271 self.tr("""<p>The REPL cannot be started.</p><p>Reason:""" 274 self.tr("""<p>The REPL cannot be started.</p><p>Reason:"""
272 """ {0}</p>""").format(reason)) 275 """ {0}</p>""").format(reason))
273 return 276 return
274 277
278 self.replEdit.clear()
279 self.dataReceived.connect(self.__processData)
280
275 if not self.__serial: 281 if not self.__serial:
276 self.replEdit.clear()
277 self.dataReceived.connect(self.__processData)
278 self.__openSerialLink() 282 self.__openSerialLink()
279 if self.__serial: 283 if self.__serial:
280 if self.__device.forceInterrupt(): 284 if self.__device.forceInterrupt():
281 # send a Ctrl-B (exit raw mode) 285 # send a Ctrl-B (exit raw mode)
282 self.__serial.write(b'\x02') 286 self.__serial.write(b'\x02')
292 Private slot to disconnect from the currently connected device. 296 Private slot to disconnect from the currently connected device.
293 """ 297 """
294 if self.__replRunning: 298 if self.__replRunning:
295 self.on_replButton_clicked() 299 self.on_replButton_clicked()
296 300
301 if self.__plotterRunning:
302 self.on_chartButton_clicked()
303
297 # TODO: add more 304 # TODO: add more
298 305
299 def __disconnect(self): 306 def __disconnectSerial(self):
300 """ 307 """
301 Private slot to disconnect the serial connection. 308 Private slot to disconnect the serial connection.
302 """ 309 """
303 self.__closeSerialLink() 310 self.__closeSerialLink()
304 self.setConnected(False) 311 self.setConnected(False)
451 def __doZoom(self, value): 458 def __doZoom(self, value):
452 """ 459 """
453 Private slot to zoom the REPL pane. 460 Private slot to zoom the REPL pane.
454 461
455 @param value zoom value 462 @param value zoom value
456 @param int 463 @type int
457 """ 464 """
458 if value < self.__currentZoom: 465 if value < self.__currentZoom:
459 self.replEdit.zoomOut(self.__currentZoom - value) 466 self.replEdit.zoomOut(self.__currentZoom - value)
460 elif value > self.__currentZoom: 467 elif value > self.__currentZoom:
461 self.replEdit.zoomIn(value - self.__currentZoom) 468 self.replEdit.zoomIn(value - self.__currentZoom)
471 portName = self.deviceTypeComboBox.itemData( 478 portName = self.deviceTypeComboBox.itemData(
472 self.deviceTypeComboBox.currentIndex(), 479 self.deviceTypeComboBox.currentIndex(),
473 self.DevicePortRole) 480 self.DevicePortRole)
474 481
475 if Globals.isWindowsPlatform(): 482 if Globals.isWindowsPlatform():
476 # return unchanged 483 # return it unchanged
477 return portName 484 return portName
478 else: 485 else:
479 # return with device path prepended 486 # return with device path prepended
480 return "/dev/{0}".format(portName) 487 return "/dev/{0}".format(portName)
481 488
525 Public method to execute a series of commands over a period of time. 532 Public method to execute a series of commands over a period of time.
526 533
527 @param commandsList list of commands to be execute on the device 534 @param commandsList list of commands to be execute on the device
528 @type list of bytes 535 @type list of bytes
529 """ 536 """
537 def remainingTask(commands):
538 self.execute(commands)
539
530 if commandsList: 540 if commandsList:
531 command = commandsList[0] 541 command = commandsList[0]
532 self.__serial.write(command) 542 self.__serial.write(command)
533 remainder = commandsList[1:] 543 remainder = commandsList[1:]
534 remainingTask = lambda commands=remainder: self.execute(commands) 544 QTimer.singleShot(2, lambda: remainingTask(remainder))
535 QTimer.singleShot(2, remainingTask)
536 545
537 @pyqtSlot() 546 @pyqtSlot()
538 def on_runButton_clicked(self): 547 def on_runButton_clicked(self):
539 """ 548 """
540 Private slot to execute the script of the active editor on the 549 Private slot to execute the script of the active editor on the
603 return 612 return
604 613
605 workspace = self.__device.getWorkspace() 614 workspace = self.__device.getWorkspace()
606 aw = e5App().getObject("ViewManager").activeWindow() 615 aw = e5App().getObject("ViewManager").activeWindow()
607 aw.saveFileAs(workspace) 616 aw.saveFileAs(workspace)
617
618 @pyqtSlot()
619 def on_chartButton_clicked(self):
620 """
621 Private slot to open a chart view to plot data received from the
622 connected device.
623 """
624 if not HAS_QTCHART:
625 # QtChart not available => fail silently
626 return
627
628 if not self.__device:
629 self.__showNoDeviceMessage()
630 return
631
632 if self.__ui is None:
633 self.__ui = e5App().getObject("UserInterface")
634
635 if self.__plotterRunning:
636 if self.__chartWidget.hasData():
637 res = E5MessageBox.okToClearData(
638 self,
639 self.tr("Unsaved Chart Data"),
640 self.tr("""The chart contains unsaved data."""),
641 self.__chartWidget.saveData)
642 if not res:
643 # abort
644 return
645
646 self.dataReceived.disconnect(self.__chartWidget.processData)
647 self.__chartWidget.dataFlood.disconnect(self.handleDataFlood)
648
649 if not self.__replRunning:
650 self.__disconnectSerial()
651
652 self.__plotterRunning = False
653 self.__device.setPlotter(False)
654 self.__ui.removeSideWidget(self.__chartWidget)
655 else:
656 ok, reason = self.__device.canStartPlotter()
657 if not ok:
658 E5MessageBox.warning(
659 self,
660 self.tr("Start Chart"),
661 self.tr("""<p>The Chart cannot be started.</p><p>Reason:"""
662 """ {0}</p>""").format(reason))
663 return
664
665 self.__chartWidget = MicroPythonGraphWidget(self)
666 self.dataReceived.connect(self.__chartWidget.processData)
667 self.__chartWidget.dataFlood.connect(self.handleDataFlood)
668
669 self.__ui.addSideWidget(self.__ui.BottomSide, self.__chartWidget,
670 UI.PixmapCache.getIcon("chart"),
671 self.tr("Chart"))
672 self.__ui.showSideWidget(self.__chartWidget)
673
674 if not self.__serial:
675 self.__openSerialLink()
676 if self.__serial:
677 if self.__device.forceInterrupt():
678 # send a Ctrl-B (exit raw mode)
679 self.__serial.write(b'\x02')
680 # send Ctrl-C (keyboard interrupt)
681 self.__serial.write(b'\x03')
682
683 self.__plotterRunning = True
684 self.__device.setPlotter(True)
685
686 @pyqtSlot()
687 def handleDataFlood(self):
688 """
689 Public slot handling a data flood from the device.
690 """
691 self.on_disconnectButton_clicked()
692 self.__device.handleDataFlood()

eric ide

mercurial