eric6/MicroPython/MicroPythonReplWidget.py

branch
micropython
changeset 7065
e3d04faced34
parent 7062
ac12da95958b
child 7066
e3d034e65afc
--- 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()

eric ide

mercurial