X-Git-Url: http://sigrok.org/gitweb/?a=blobdiff_plain;f=mainwindow.py;h=7398a5a84c5da87bb255efcc186e524785c41f02;hb=1879265add9b3abfec2d651c452fbf5be20b236d;hp=2af07b2a0018bf31b821a9a268e7fa802b03c297;hpb=480cdb7bc04548aec46b7342d6fa1acafa60331c;p=sigrok-meter.git diff --git a/mainwindow.py b/mainwindow.py index 2af07b2..7398a5a 100644 --- a/mainwindow.py +++ b/mainwindow.py @@ -20,13 +20,17 @@ ## import datamodel +import multiplotwidget import os.path import qtcompat import samplingthread import textwrap +import time +import util QtCore = qtcompat.QtCore QtGui = qtcompat.QtGui +pyqtgraph = qtcompat.pyqtgraph class EmptyMessageListView(QtGui.QListView): '''List view that shows a message if the model im empty.''' @@ -48,6 +52,12 @@ class EmptyMessageListView(QtGui.QListView): class MainWindow(QtGui.QMainWindow): '''The main window of the application.''' + # Number of seconds that the plots display. + BACKLOG = 30 + + # Update interval of the plots in milliseconds. + UPDATEINTERVAL = 100 + def __init__(self, context, drivers): super(self.__class__, self).__init__() @@ -67,7 +77,7 @@ class MainWindow(QtGui.QMainWindow): def setup_ui(self): self.setWindowTitle('sigrok-meter') # Resizing the listView below will increase this again. - self.resize(10, 10) + self.resize(350, 10) p = os.path.abspath(os.path.dirname(__file__)) p = os.path.join(p, 'sigrok-logo-notext.png') @@ -103,8 +113,120 @@ class MainWindow(QtGui.QMainWindow): self.listView.setUniformItemSizes(True) self.listView.setMinimumSize(self.delegate.sizeHint()) - self.setCentralWidget(self.listView) + self.plotwidget = multiplotwidget.MultiPlotWidget(self) + self.plotwidget.plotHidden.connect(self._on_plotHidden) + + # Maps from 'unit' to the corresponding plot. + self._plots = {} + # Maps from '(plot, device)' to the corresponding curve. + self._curves = {} + + self.splitter = QtGui.QSplitter(QtCore.Qt.Horizontal); + self.splitter.addWidget(self.listView) + self.splitter.addWidget(self.plotwidget) + self.splitter.setStretchFactor(0, 0) + self.splitter.setStretchFactor(1, 1) + + self.setCentralWidget(self.splitter) self.centralWidget().setContentsMargins(0, 0, 0, 0) + self.resize(800, 500) + + self.startTimer(MainWindow.UPDATEINTERVAL) + + def _getPlot(self, unit): + '''Looks up or creates a new plot for 'unit'.''' + + if unit in self._plots: + return self._plots[unit] + + # create a new plot for the unit + plot = self.plotwidget.addPlot() + plot.yaxis.setLabel(util.quantity_from_unit(unit), units=util.format_unit(unit)) + plot.view.setXRange(-MainWindow.BACKLOG, 0, update=False) + plot.view.setYRange(-1, 1) + plot.view.enableAutoRange(axis=pyqtgraph.ViewBox.YAxis) + # lock to the range calculated by the view using additional padding, + # looks nicer this way + r = plot.view.viewRange() + plot.view.setLimits(xMin=r[0][0], xMax=r[0][1]) + + self._plots[unit] = plot + return plot + + def _getCurve(self, plot, deviceID): + '''Looks up or creates a new curve for '(plot, deviceID)'.''' + + key = (id(plot), deviceID) + if key in self._curves: + return self._curves[key] + + # create a new curve + curve = pyqtgraph.PlotDataItem( + antialias=True, + symbolPen=pyqtgraph.mkPen(QtGui.QColor(QtCore.Qt.black)), + symbolBrush=pyqtgraph.mkBrush(QtGui.QColor(QtCore.Qt.black)), + symbolSize=1 + ) + plot.view.addItem(curve) + + self._curves[key] = curve + return curve + + def timerEvent(self, event): + '''Periodically updates all graphs.''' + + self._updatePlots() + + def _updatePlots(self): + '''Updates all plots.''' + + # loop over all devices and channels + for row in range(self.model.rowCount()): + idx = self.model.index(row, 0) + deviceID = self.model.data(idx, + datamodel.MeasurementDataModel.idRole) + deviceID = tuple(deviceID) # PySide returns a list. + traces = self.model.data(idx, + datamodel.MeasurementDataModel.tracesRole) + + for unit, trace in traces.items(): + now = time.time() + + # remove old samples + l = now - MainWindow.BACKLOG + while trace.samples and trace.samples[0][0] < l: + trace.samples.pop(0) + + plot = self._getPlot(unit) + if not plot.visible: + if trace.new: + self.plotwidget.showPlot(plot) + + if plot.visible: + xdata = [s[0] - now for s in trace.samples] + ydata = [s[1] for s in trace.samples] + + color = self.model.data(idx, + datamodel.MeasurementDataModel.colorRole) + + curve = self._getCurve(plot, deviceID) + curve.setPen(pyqtgraph.mkPen(color=color)) + curve.setData(xdata, ydata) + + @QtCore.Slot(multiplotwidget.Plot) + def _on_plotHidden(self, plot): + plotunit = [u for u, p in self._plots.items() if p == plot][0] + + # Mark all traces of all devices/channels with the same unit as the + # plot as "old" ('trace.new = False'). As soon as a new sample arrives + # on one trace, the plot will be shown again + for row in range(self.model.rowCount()): + idx = self.model.index(row, 0) + traces = self.model.data(idx, datamodel.MeasurementDataModel.tracesRole) + + for traceunit, trace in traces.items(): + if traceunit == plotunit: + trace.new = False def closeEvent(self, event): self.thread.stop()