--- a/eric6/MicroPython/MicroPythonReplWidget.py Thu Jul 11 19:48:14 2019 +0200 +++ b/eric6/MicroPython/MicroPythonReplWidget.py Tue Jul 16 20:12:53 2019 +0200 @@ -22,11 +22,6 @@ HAS_QTSERIALPORT = True except ImportError: HAS_QTSERIALPORT = False -try: - from PyQt5.QtChart import QChart # __IGNORE_WARNING__ - HAS_QTCHART = True -except ImportError: - HAS_QTCHART = False from E5Gui.E5ZoomWidget import E5ZoomWidget from E5Gui import E5MessageBox, E5FileDialog @@ -35,6 +30,11 @@ from .Ui_MicroPythonReplWidget import Ui_MicroPythonReplWidget from . import MicroPythonDevices +try: + from .MicroPythonGraphWidget import MicroPythonGraphWidget + HAS_QTCHART = True +except ImportError: + HAS_QTCHART = False import Globals import UI.PixmapCache @@ -97,6 +97,8 @@ self.__zoomWidget.valueChanged.connect(self.__doZoom) self.__currentZoom = 0 + self.__ui = None + self.__serial = None self.__device = None self.setConnected(False) @@ -182,8 +184,8 @@ """ Public method to set the enabled state of the various action buttons. - @param kwargs keyword arguments containg the enabled states (keys are - 'run', 'repl', 'files', 'chart', 'open', 'save' + @keyparam kwargs keyword arguments containg the enabled states (keys + are 'run', 'repl', 'files', 'chart', 'open', 'save' @type dict """ if "open" in kwargs: @@ -202,7 +204,7 @@ @pyqtSlot(QPoint) def __showContextMenu(self, pos): """ - Privat slot to show the REPL context menu. + Private slot to show the REPL context menu. @param pos position to show the menu at @type QPoint @@ -259,7 +261,8 @@ if self.__replRunning: self.dataReceived.disconnect(self.__processData) - self.__disconnect() + if not self.__plotterRunning: + self.__disconnectSerial() self.__replRunning = False self.__device.setRepl(False) else: @@ -271,10 +274,11 @@ self.tr("""<p>The REPL cannot be started.</p><p>Reason:""" """ {0}</p>""").format(reason)) return - + + self.replEdit.clear() + self.dataReceived.connect(self.__processData) + if not self.__serial: - self.replEdit.clear() - self.dataReceived.connect(self.__processData) self.__openSerialLink() if self.__serial: if self.__device.forceInterrupt(): @@ -294,9 +298,12 @@ if self.__replRunning: self.on_replButton_clicked() + if self.__plotterRunning: + self.on_chartButton_clicked() + # TODO: add more - def __disconnect(self): + def __disconnectSerial(self): """ Private slot to disconnect the serial connection. """ @@ -453,7 +460,7 @@ Private slot to zoom the REPL pane. @param value zoom value - @param int + @type int """ if value < self.__currentZoom: self.replEdit.zoomOut(self.__currentZoom - value) @@ -473,7 +480,7 @@ self.DevicePortRole) if Globals.isWindowsPlatform(): - # return unchanged + # return it unchanged return portName else: # return with device path prepended @@ -527,12 +534,14 @@ @param commandsList list of commands to be execute on the device @type list of bytes """ + def remainingTask(commands): + self.execute(commands) + if commandsList: command = commandsList[0] self.__serial.write(command) remainder = commandsList[1:] - remainingTask = lambda commands=remainder: self.execute(commands) - QTimer.singleShot(2, remainingTask) + QTimer.singleShot(2, lambda: remainingTask(remainder)) @pyqtSlot() def on_runButton_clicked(self): @@ -605,3 +614,79 @@ workspace = self.__device.getWorkspace() aw = e5App().getObject("ViewManager").activeWindow() aw.saveFileAs(workspace) + + @pyqtSlot() + def on_chartButton_clicked(self): + """ + Private slot to open a chart view to plot data received from the + connected device. + """ + if not HAS_QTCHART: + # QtChart not available => fail silently + return + + if not self.__device: + self.__showNoDeviceMessage() + return + + if self.__ui is None: + self.__ui = e5App().getObject("UserInterface") + + if self.__plotterRunning: + if self.__chartWidget.hasData(): + res = E5MessageBox.okToClearData( + self, + self.tr("Unsaved Chart Data"), + self.tr("""The chart contains unsaved data."""), + self.__chartWidget.saveData) + if not res: + # abort + return + + self.dataReceived.disconnect(self.__chartWidget.processData) + self.__chartWidget.dataFlood.disconnect(self.handleDataFlood) + + if not self.__replRunning: + self.__disconnectSerial() + + self.__plotterRunning = False + self.__device.setPlotter(False) + self.__ui.removeSideWidget(self.__chartWidget) + else: + ok, reason = self.__device.canStartPlotter() + if not ok: + E5MessageBox.warning( + self, + self.tr("Start Chart"), + self.tr("""<p>The Chart cannot be started.</p><p>Reason:""" + """ {0}</p>""").format(reason)) + return + + self.__chartWidget = MicroPythonGraphWidget(self) + self.dataReceived.connect(self.__chartWidget.processData) + self.__chartWidget.dataFlood.connect(self.handleDataFlood) + + self.__ui.addSideWidget(self.__ui.BottomSide, self.__chartWidget, + UI.PixmapCache.getIcon("chart"), + self.tr("Chart")) + self.__ui.showSideWidget(self.__chartWidget) + + if not self.__serial: + self.__openSerialLink() + if self.__serial: + if self.__device.forceInterrupt(): + # send a Ctrl-B (exit raw mode) + self.__serial.write(b'\x02') + # send Ctrl-C (keyboard interrupt) + self.__serial.write(b'\x03') + + self.__plotterRunning = True + self.__device.setPlotter(True) + + @pyqtSlot() + def handleDataFlood(self): + """ + Public slot handling a data flood from the device. + """ + self.on_disconnectButton_clicked() + self.__device.handleDataFlood()