implementing pyqtgraph for live data graphing

EOF picture EOF · Jul 30, 2013 · Viewed 14.5k times · Source

I am trying to get a live plot of data as it is being collected by an instrument using pyqtgraph.

The data collection is handled by the main process this is then passed over a connection to a subprocess that plots it. I am just trying to pass the new data and have it update when the new data is passed.

I have tried to implement this by putting the connection read inside the Qt Timer update loop but then I have difficulty passing the graph data (which I want to append to) between successive update cycles. I omitted from the code below, but basically I want to join up successive lots of connData, I thought using a global would work, but for some reason I can't implement that here.

EDIT 1: The problem that I have compared with the pyqtgraph examples seems to be passing data to the update process of the Qtimer, I get:

NameError: global name 'CurveData' is not defined

The reason I tried to do it this way rather than using the multiprocessing module of pyqtgraph is that I did not want to pass the entire data set over the connection each time (which I thought would be necessary). Thanks!

EDIT 2: Is there documentation for pyqtgraph.multiprocess somewhere?

(this is the original code provided)

import multiprocessing
import numpy as np
from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph as pg
import pyqtgraph.opengl as gl
import sys
import os

def MakeGraph(conn):

    win = pg.GraphicsWindow(title = "test")
    win.resize(300,300)

    p1 = win.addPlot(title = "test")

    curve = p1.plot(pen = 'y')
    timer = QtCore.QTimer()

    def Update():
        try:
            ConnData = conn.recv()
            ConnData = [float(i) for i in ConnData]
            curve.setData(ConnData)
        except EOFError:
            print "Graph connection closed\n"
            timer.stop()
            QtGui.QApplication.quit()

    timer.timeout.connect(Update)
    timer.start(0)

    if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
            QtGui.QApplication.instance().exec_()

modified version:

import multiprocessing
import numpy as np
from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph as pg
import pyqtgraph.opengl as gl
import sys
import os

def MakeGraph(conn):

    win = pg.GraphicsWindow(title = "test")
    win.resize(300,300)

    p1 = win.addPlot(title = "test")

    curve = p1.plot(pen = 'y')
    timer = QtCore.QTimer()
    CurveData = []

    def Update():
        global CurveData
        try:
            ConnData = conn.recv()
            ConnData = [float(i) for i in ConnData]
            CurveData = np.append(CurveData,ConnData)
            curve.setData(CurveData)
        except EOFError:
            print "Graph connection closed\n"
            timer.stop()
            QtGui.QApplication.quit()

    timer.timeout.connect(Update)
    timer.start(0)

    if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
            QtGui.QApplication.instance().exec_()

Answer

Luke picture Luke · Jul 30, 2013

You have not explained what does not work about the example posted. Are there error messages? Does the process fail to receive messages? Are the plot results somehow different from what you expect?

As an initial answer, I'll recommend a different approach: use pyqtgraph's built-in multiprocessing functionality. If you run this code from your main process, it will give you a proxy to a plot window and curve running in a new process. Any methods you call on the proxies will be forwarded to the remote process:

import pyqtgraph as pg
pg.mkQApp()

# Create remote process with a plot window
import pyqtgraph.multiprocess as mp
proc = mp.QtProcess()
rpg = proc._import('pyqtgraph')
plotwin = rpg.plot()
curve = plotwin.plot(pen='y')

# create an empty list in the remote process
data = proc.transfer([])

# Send new data to the remote process and plot it
# We use the special argument _callSync='off' because we do
# not want to wait for a return value.
data.extend([1,5,2,4,3], _callSync='off')
curve.setData(y=data, _callSync='off')

Note that all of the objects we created here--rpg, plotwin, curve, and data--are proxies for real objects in the remote process. Thus almost anything you can do with the usual pyqtgraph classes can also be done with these objects and the results will appear in the remote process. For example:

# Local code:
win = pg.GraphicsWindow()
p1 = win.addPlot()
p2 = win.addPlot()

# Remote code:
win = rpg.GraphicsWindow()
p1 = win.addPlot()
p2 = win.addPlot()

The only difference for the two examples is the starting point--pg for the local pyqtgraph module, and rpg for the remote module.