src/eric7/MicroPython/MicroPythonGraphWidget.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9209
b99e7fd55fd3
child 9413
80c06d472826
--- a/src/eric7/MicroPython/MicroPythonGraphWidget.py	Wed Jul 13 11:16:20 2022 +0200
+++ b/src/eric7/MicroPython/MicroPythonGraphWidget.py	Wed Jul 13 14:55:47 2022 +0200
@@ -17,8 +17,14 @@
 from PyQt6.QtCore import pyqtSignal, pyqtSlot, Qt
 from PyQt6.QtGui import QPainter
 from PyQt6.QtWidgets import (
-    QWidget, QHBoxLayout, QVBoxLayout, QToolButton, QSizePolicy, QSpacerItem,
-    QLabel, QSpinBox
+    QWidget,
+    QHBoxLayout,
+    QVBoxLayout,
+    QToolButton,
+    QSizePolicy,
+    QSpacerItem,
+    QLabel,
+    QSpinBox,
 )
 from PyQt6.QtCharts import QChartView, QChart, QLineSeries, QValueAxis
 
@@ -32,99 +38,103 @@
 class MicroPythonGraphWidget(QWidget):
     """
     Class implementing the MicroPython graph widget.
-    
+
     @signal dataFlood emitted to indicate, that too much data is received
     """
+
     dataFlood = pyqtSignal()
-    
+
     def __init__(self, parent=None):
         """
         Constructor
-        
+
         @param parent reference to the parent widget
         @type QWidget
         """
         super().__init__(parent)
-        
+
         self.__layout = QHBoxLayout()
         self.__layout.setContentsMargins(2, 2, 2, 2)
         self.setLayout(self.__layout)
-        
+
         self.__chartView = QChartView(self)
         self.__chartView.setSizePolicy(
-            QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
+            QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding
+        )
         self.__layout.addWidget(self.__chartView)
-        
+
         self.__verticalLayout = QVBoxLayout()
         self.__verticalLayout.setContentsMargins(0, 0, 0, 0)
         self.__layout.addLayout(self.__verticalLayout)
-        
+
         self.__saveButton = QToolButton(self)
         self.__saveButton.setIcon(UI.PixmapCache.getIcon("fileSave"))
         self.__saveButton.setToolTip(self.tr("Press to save the raw data"))
         self.__saveButton.clicked.connect(self.on_saveButton_clicked)
         self.__verticalLayout.addWidget(self.__saveButton)
-        self.__verticalLayout.setAlignment(self.__saveButton,
-                                           Qt.AlignmentFlag.AlignHCenter)
-        
-        spacerItem = QSpacerItem(20, 20, QSizePolicy.Policy.Minimum,
-                                 QSizePolicy.Policy.Expanding)
+        self.__verticalLayout.setAlignment(
+            self.__saveButton, Qt.AlignmentFlag.AlignHCenter
+        )
+
+        spacerItem = QSpacerItem(
+            20, 20, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding
+        )
         self.__verticalLayout.addItem(spacerItem)
-        
+
         label = QLabel(self.tr("max. X:"))
         self.__verticalLayout.addWidget(label)
-        self.__verticalLayout.setAlignment(label,
-                                           Qt.AlignmentFlag.AlignHCenter)
-        
+        self.__verticalLayout.setAlignment(label, Qt.AlignmentFlag.AlignHCenter)
+
         self.__maxX = 100
         self.__maxXSpinBox = QSpinBox()
         self.__maxXSpinBox.setMinimum(100)
         self.__maxXSpinBox.setMaximum(1000)
         self.__maxXSpinBox.setSingleStep(100)
-        self.__maxXSpinBox.setToolTip(self.tr(
-            "Enter the maximum number of data points to be plotted."))
+        self.__maxXSpinBox.setToolTip(
+            self.tr("Enter the maximum number of data points to be plotted.")
+        )
         self.__maxXSpinBox.setValue(self.__maxX)
         self.__maxXSpinBox.setAlignment(Qt.AlignmentFlag.AlignRight)
         self.__verticalLayout.addWidget(self.__maxXSpinBox)
-        
+
         # holds the data to be checked for plotable data
         self.__inputBuffer = []
         # holds the raw data
         self.__rawData = []
         self.__dirty = False
-        
+
         self.__maxY = 1000
         self.__flooded = False  # flag indicating a data flood
-        
+
         self.__data = [deque([0] * self.__maxX)]
         self.__series = [QLineSeries()]
-        
+
         # Y-axis ranges
         self.__yRanges = [1, 5, 10, 25, 50, 100, 250, 500, 1000]
-        
+
         # setup the chart
         self.__chart = QChart()
         self.__chart.legend().hide()
         self.__chart.addSeries(self.__series[0])
-        
+
         self.__axisX = QValueAxis()
         self.__axisX.setLabelFormat("time")
         self.__chart.addAxis(self.__axisX, Qt.AlignmentFlag.AlignBottom)
         self.__series[0].attachAxis(self.__axisX)
         self.__axisX.setRange(0, self.__maxX)
-        
+
         self.__axisY = QValueAxis()
         self.__axisY.setLabelFormat("%d")
         self.__chart.addAxis(self.__axisY, Qt.AlignmentFlag.AlignLeft)
         self.__series[0].attachAxis(self.__axisY)
         self.__axisY.setRange(-self.__maxY, self.__maxY)
-        
+
         self.__chartView.setChart(self.__chart)
         self.__chartView.setRenderHint(QPainter.RenderHint.Antialiasing)
         self.preferencesChanged()
-        
+
         self.__maxXSpinBox.valueChanged.connect(self.__handleMaxXChanged)
-    
+
     @pyqtSlot()
     def preferencesChanged(self):
         """
@@ -140,17 +150,17 @@
                 self.__chart.setTheme(QChart.ChartTheme.ChartThemeLight)
         else:
             self.__chart.setTheme(chartColorTheme)
-    
+
     @pyqtSlot(bytes)
     def processData(self, data):
         """
         Public slot to process the raw data.
-        
+
         It takes raw bytes, checks the data for a valid tuple of ints or
         floats and adds the data to the graph. If the the length of the bytes
         data is greater than 1024 then a dataFlood signal is emitted to ensure
         eric can take action to remain responsive.
-        
+
         @param data raw data received from the connected device via the main
             device widget
         @type bytes
@@ -158,19 +168,19 @@
         # flooding guard
         if self.__flooded:
             return
-        
+
         if len(data) > 1024:
             self.__flooded = True
             self.dataFlood.emit()
             return
-        
+
         # disable the inputs while processing data
         self.__saveButton.setEnabled(False)
         self.__maxXSpinBox.setEnabled(False)
-        
+
         data = data.replace(b"\r\n", b"\n").replace(b"\r", b"\n")
         self.__inputBuffer.append(data)
-        
+
         # check if the data contains a Python tuple containing numbers (int
         # or float) on a single line
         inputBytes = b"".join(self.__inputBuffer)
@@ -179,7 +189,7 @@
             if not line.endswith(b"\n"):
                 # incomplete line (last line); skip it
                 break
-            
+
             line = line.strip()
             if line.startswith(b"(") and line.endswith(b")"):
                 # it may be a tuple we are interested in
@@ -197,32 +207,32 @@
                         continue
                 if values:
                     self.__addData(tuple(values))
-        
+
         self.__inputBuffer = []
         if lines[-1] and not lines[-1].endswith(b"\n"):
             # Append any left over bytes for processing next time data is
             # received.
             self.__inputBuffer.append(lines[-1])
-        
+
         # re-enable the inputs
         self.__saveButton.setEnabled(True)
         self.__maxXSpinBox.setEnabled(True)
-    
+
     def __addData(self, values):
         """
         Private method to add a tuple of values to the graph.
-        
+
         It ensures there are the required number of line series, adds the data
         to the line series and updates the range of the chart so the chart
         displays nicely.
-        
+
         @param values tuple containing the data to be added
         @type tuple of int or float
         """
         # store incoming data to be able to dump it as CSV upon request
         self.__rawData.append(values)
         self.__dirty = True
-        
+
         # check number of incoming values and adjust line series accordingly
         if len(values) != len(self.__series):
             valuesLen = len(values)
@@ -242,16 +252,17 @@
                     self.__chart.removeSeries(oldSeries)
                 self.__series = self.__series[:valuesLen]
                 self.__data = self.__data[:valuesLen]
-        
+
         # add the new values to the display and compute the maximum range
         maxRanges = []
         for index, value in enumerate(values):
             self.__data[index].appendleft(value)
-            maxRanges.append(max([max(self.__data[index]),
-                                  abs(min(self.__data[index]))]))
+            maxRanges.append(
+                max([max(self.__data[index]), abs(min(self.__data[index]))])
+            )
             if len(self.__data[index]) > self.__maxX:
                 self.__data[index].pop()
-        
+
         # re-scale the y-axis
         maxYRange = max(maxRanges)
         yRange = bisect.bisect_left(self.__yRanges, maxYRange)
@@ -262,13 +273,13 @@
         elif maxYRange < self.__maxY / 2:
             self.__maxY /= 2
         self.__axisY.setRange(-self.__maxY, self.__maxY)
-        
+
         # ensure that floats are used to label the y-axis if the range is small
         if self.__maxY <= 5:
             self.__axisY.setLabelFormat("%2.2f")
         else:
             self.__axisY.setLabelFormat("%d")
-        
+
         # update the line series
         for index, series in enumerate(self.__series):
             series.clear()
@@ -278,49 +289,49 @@
                 xyValues.append((x, value))
             for xy in xyValues:
                 series.append(*xy)
-    
+
     @pyqtSlot()
     def on_saveButton_clicked(self):
         """
         Private slot to save the raw data to a CSV file.
         """
         self.saveData()
-    
+
     def hasData(self):
         """
         Public method to check, if the chart contains some valid data.
-        
+
         @return flag indicating valid data
         @rtype bool
         """
         return len(self.__rawData) > 0
-    
+
     def isDirty(self):
         """
         Public method to check, if the chart contains unsaved data.
-        
+
         @return flag indicating unsaved data
         @rtype bool
         """
         return self.hasData() and self.__dirty
-    
+
     def saveData(self):
         """
         Public method to save the dialog's raw data.
-        
+
         @return flag indicating success
         @rtype bool
         """
         baseDir = (
-            Preferences.getMicroPython("MpyWorkspace") or
-            Preferences.getMultiProject("Workspace") or
-            os.path.expanduser("~")
+            Preferences.getMicroPython("MpyWorkspace")
+            or Preferences.getMultiProject("Workspace")
+            or os.path.expanduser("~")
         )
         dataDir = os.path.join(baseDir, "data_capture")
-        
+
         if not os.path.exists(dataDir):
             os.makedirs(dataDir)
-        
+
         # save the raw data as a CSV file
         fileName = "{0}.csv".format(time.strftime("%Y%m%d-%H%M%S"))
         fullPath = os.path.join(dataDir, fileName)
@@ -328,7 +339,7 @@
             with open(fullPath, "w") as csvFile:
                 csvWriter = csv.writer(csvFile)
                 csvWriter.writerows(self.__rawData)
-            
+
             self.__dirty = False
             return True
         except OSError as err:
@@ -337,15 +348,16 @@
                 self.tr("Save Chart Data"),
                 self.tr(
                     """<p>The chart data could not be saved into file"""
-                    """ <b>{0}</b>.</p><p>Reason: {1}</p>""").format(
-                    fullPath, str(err)))
+                    """ <b>{0}</b>.</p><p>Reason: {1}</p>"""
+                ).format(fullPath, str(err)),
+            )
             return False
-    
+
     @pyqtSlot(int)
     def __handleMaxXChanged(self, value):
         """
         Private slot handling a change of the max. X spin box.
-        
+
         @param value value of the spin box
         @type int
         """
@@ -363,6 +375,6 @@
             for deq in self.__data:
                 data.append(deque(list(deq)[:value]))
             self.__data = data
-        
+
         self.__maxX = value
         self.__axisX.setRange(0, self.__maxX)

eric ide

mercurial