Updating line plots in pyqtgraph plotwidget
-
I am trying to create 3 graphs (temperature, pitch, roll) for each of my devices. Right now I have 3 devices, so i would want a separate line for each graph.
I am trying to update the data when new data comes in from a SSE, which does get called and gets to the update function. For some reason, setData is not working for me, keep getting errors involving not being finite or nan, even though the data is clean.
Also, I am not sure how to update only one line at a time on each graph, if that is possible with setData.
I tried to get the code below to only have relevant parts. you can assume the server side event is working, getting the data, and calling update graphs inside a pyqtSlot (i.e. on the same thread as the GUI). Here is my code:
from PyQt5 import QtWidgets, QtCore, uic from pyqtgraph import PlotWidget, plot import pyqtgraph as pg from sseclient import SSEClient import requests import json import threading import queue import time import multiprocessing as mp graph_colors = ['white', 'green', 'red', 'blue', 'orange', 'purple'] class UI(QtWidgets.QMainWindow): add_row_s = QtCore.pyqtSignal(str, str, str) create_devices_ui_s = QtCore.pyqtSignal(list) def __init__(self): super(UI, self).__init__() uic.loadUi('ui/uhmss_ui.ui', self) self._run_flag = True self.device_data = {} self.create_devices_ui_s.connect(self.create_devices_ui) # plot data: x, y values self.graphlayout = self.findChild(QtWidgets.QVBoxLayout, 'graphlayout') self.temperaturegraph = pg.PlotWidget() self.temperaturegraph.addLegend() self.temperaturegraph.setTitle("Temperature") self.temperaturegraph.setLabel("left", "Temperature (ºC)") self.temperaturegraph.setYRange(-20, 80) self.temperaturegraph.setXRange(0, 10) self.pitchgraph = pg.PlotWidget() self.pitchgraph.setTitle("Pitch") self.pitchgraph.addLegend() self.pitchgraph.setLabel("left", "Degrees") self.pitchgraph.setYRange(-90, 90) self.rollgraph = pg.PlotWidget() self.rollgraph.setTitle("Roll") self.rollgraph.addLegend() self.rollgraph.setLabel("left", "Degrees") self.rollgraph.setYRange(-180, 180) self.graphlayout.addWidget(self.temperaturegraph) self.graphlayout.addWidget(self.pitchgraph) self.graphlayout.addWidget(self.rollgraph) def device_query(self): #gets device data, can assume this works self.create_devices_us_s.emit(devices) @QtCore.pyqtSlot(list) def create_devices_ui(self, devices): self.device_data = {} for i in range(0, len(devices)): device = devices[i] id = device["id"] name = device["name"] self.device_data[id] = {} self.device_data[id]["name"] = name self.device_data[id]["color"] = graph_colors[i] self.device_data[id]['temperatureplot'] = self.temperaturegraph.getPlotItem().plot() self.device_data[id]['pitchplot'] = self.pitchgraph.getPlotItem().plot() self.device_data[id]['rollplot'] = self.rollgraph.getPlotItem().plot() self.device_data[id]["Temperature"] = [] self.device_data[id]["Pitch"] = [] self.device_data[id]["Roll"] = [] # update graphs, called from inside GUI thread, using signal/slot @QtCore.pyqtSlot(str, str, str) def update_graphs(self, id, event, data): device = self.device_data[id] match(event): case "Temperature": tempdata = device["Temperature"] tempdata.append(data) plot = device["temperatureplot"] plot.setData(range(len(tempdata)), tempdata) case "Roll": tempdata = device["Roll"] tempdata.append(data) plot = device["rollplot"] plot.setData(range(len(tempdata)), tempdata) case "Pitch": tempdata = device["Pitch"] tempdata.append(data) plot = device["pitchplot"] plot.setData(range(len(tempdata)), tempdata)
-
I am trying to create 3 graphs (temperature, pitch, roll) for each of my devices. Right now I have 3 devices, so i would want a separate line for each graph.
I am trying to update the data when new data comes in from a SSE, which does get called and gets to the update function. For some reason, setData is not working for me, keep getting errors involving not being finite or nan, even though the data is clean.
Also, I am not sure how to update only one line at a time on each graph, if that is possible with setData.
I tried to get the code below to only have relevant parts. you can assume the server side event is working, getting the data, and calling update graphs inside a pyqtSlot (i.e. on the same thread as the GUI). Here is my code:
from PyQt5 import QtWidgets, QtCore, uic from pyqtgraph import PlotWidget, plot import pyqtgraph as pg from sseclient import SSEClient import requests import json import threading import queue import time import multiprocessing as mp graph_colors = ['white', 'green', 'red', 'blue', 'orange', 'purple'] class UI(QtWidgets.QMainWindow): add_row_s = QtCore.pyqtSignal(str, str, str) create_devices_ui_s = QtCore.pyqtSignal(list) def __init__(self): super(UI, self).__init__() uic.loadUi('ui/uhmss_ui.ui', self) self._run_flag = True self.device_data = {} self.create_devices_ui_s.connect(self.create_devices_ui) # plot data: x, y values self.graphlayout = self.findChild(QtWidgets.QVBoxLayout, 'graphlayout') self.temperaturegraph = pg.PlotWidget() self.temperaturegraph.addLegend() self.temperaturegraph.setTitle("Temperature") self.temperaturegraph.setLabel("left", "Temperature (ºC)") self.temperaturegraph.setYRange(-20, 80) self.temperaturegraph.setXRange(0, 10) self.pitchgraph = pg.PlotWidget() self.pitchgraph.setTitle("Pitch") self.pitchgraph.addLegend() self.pitchgraph.setLabel("left", "Degrees") self.pitchgraph.setYRange(-90, 90) self.rollgraph = pg.PlotWidget() self.rollgraph.setTitle("Roll") self.rollgraph.addLegend() self.rollgraph.setLabel("left", "Degrees") self.rollgraph.setYRange(-180, 180) self.graphlayout.addWidget(self.temperaturegraph) self.graphlayout.addWidget(self.pitchgraph) self.graphlayout.addWidget(self.rollgraph) def device_query(self): #gets device data, can assume this works self.create_devices_us_s.emit(devices) @QtCore.pyqtSlot(list) def create_devices_ui(self, devices): self.device_data = {} for i in range(0, len(devices)): device = devices[i] id = device["id"] name = device["name"] self.device_data[id] = {} self.device_data[id]["name"] = name self.device_data[id]["color"] = graph_colors[i] self.device_data[id]['temperatureplot'] = self.temperaturegraph.getPlotItem().plot() self.device_data[id]['pitchplot'] = self.pitchgraph.getPlotItem().plot() self.device_data[id]['rollplot'] = self.rollgraph.getPlotItem().plot() self.device_data[id]["Temperature"] = [] self.device_data[id]["Pitch"] = [] self.device_data[id]["Roll"] = [] # update graphs, called from inside GUI thread, using signal/slot @QtCore.pyqtSlot(str, str, str) def update_graphs(self, id, event, data): device = self.device_data[id] match(event): case "Temperature": tempdata = device["Temperature"] tempdata.append(data) plot = device["temperatureplot"] plot.setData(range(len(tempdata)), tempdata) case "Roll": tempdata = device["Roll"] tempdata.append(data) plot = device["rollplot"] plot.setData(range(len(tempdata)), tempdata) case "Pitch": tempdata = device["Pitch"] tempdata.append(data) plot = device["pitchplot"] plot.setData(range(len(tempdata)), tempdata)
@kgenbio said in Updating line plots in pyqtgraph plotwidget:
For some reason, setData is not working for me, keep getting errors involving not being finite or nan, even though the data is clean.
Why don't you
print(tempdata)
immediately above eachplot.setData(range(len(tempdata)), tempdata)
? -
@kgenbio said in Updating line plots in pyqtgraph plotwidget:
For some reason, setData is not working for me, keep getting errors involving not being finite or nan, even though the data is clean.
Why don't you
print(tempdata)
immediately above eachplot.setData(range(len(tempdata)), tempdata)
?Hello Jon, I did do just what you said and thought the data looked fine. while getting you an example together... I see it.
All of the values are strings instead of numbers because i am parsing them from a web request where they come over as string values.
welp silly mistake on my end.
But to my second question, what is the best way to dynamically add more devices to the list as a new line, update that line specifically with new data, and have the legend update.