# ***************************************************************************** # Copyright (c) 2015-2024 by the authors, see LICENSE # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation; either version 2 of the License, or (at your option) any later # version. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along with # this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Module authors: # Alexander Zaft # # ***************************************************************************** import time from frappy.gui.qt import QLabel, Qt, QVBoxLayout, QWidget, pyqtSignal from frappy.gui.util import Colors from frappy.lib import delayed_import np = delayed_import('numpy') pg = delayed_import('pyqtgraph') def getPlotWidget(parent): if pg: pg.setConfigOption('background', Colors.colors['plot-bg']) pg.setConfigOption('foreground', Colors.colors['plot-fg']) if pg is None: window = PlotPlaceHolderWidget(parent) else: window = PlotWidget(parent) window.setWindowFlags(Qt.WindowType.Window) return window class PlotPlaceHolderWidget(QWidget): closed = pyqtSignal(object) def __init__(self, parent=None): super().__init__(parent) l = QVBoxLayout() label = QLabel("pyqtgraph is not installed!") label.setAlignment(Qt.AlignmentFlag.AlignCenter) l.addWidget(label) self.setLayout(l) self.setMinimumWidth(300) self.setMinimumHeight(150) self.curves = {} def addCurve(self, node, module, param): pass def setCurveColor(self, module, param, color): pass def update(self, module, param, value): pass def closeEvent(self, event): self.closed.emit(self) event.accept() # TODO: # - timer-based updates when receiving no updates from the node # in order to make slower updates not jump that much? # - remove curves again class PlotWidget(QWidget): closed = pyqtSignal(object) def __init__(self, parent=None): super().__init__(parent) self.win = pg.GraphicsLayoutWidget() self.curves = {} self.data = {} self.timer = pg.QtCore.QTimer() self.timer.timeout.connect(self.scrollUpdate) # TODO: Shoud this scrolling be done? or with configuration? self.timer.start(100) self.plot = self.win.addPlot() self.plot.addLegend() self.plot.setAxisItems({'bottom': pg.DateAxisItem()}) self.plot.setLabel('bottom', 'Time') self.plot.setLabel('left', 'Value') l = QVBoxLayout() l.addWidget(self.win) self.setLayout(l) def addCurve(self, node, module, param): paramData = node._getDescribingParameterData(module, param) name = f'{module}:{param}' unit = paramData.get('unit', '') if unit: unit = '/' + unit curve = self.plot.plot(name=f'{name}{unit}') if 'min' in paramData and 'max' in paramData: curve.setXRange(paramData['min'], paramData['max']) curve.setDownsampling(method='peak') self.data[name] = (np.array([]),np.array([])) self.curves[name] = curve node.newData.connect(self.update) def setCurveColor(self, module, param, color): curve = self.curves[f'{module}:{param}'] curve.setPen(color) def scrollUpdate(self): for cname, curve in self.curves.items(): x,y = self.data[cname] x = np.append(x, time.time()) y = np.append(y, y[-1]) curve.setData(x,y) def update(self, module, param, value): name = f'{module}:{param}' if name not in self.curves: return curve = self.curves[name] x,y = self.data[name] x = np.append(x, value.timestamp) y = np.append(y, float(value.value)) self.data[name] = (x,y) curve.setData(x,y) def closeEvent(self, event): self.closed.emit(self) event.accept()