PyQtGraph

Fast Online Plotting in Python


"PyQtGraph is a pure-python graphics and GUI library built on PyQt4 / PySide and numpy. It is intended for use in mathematics / scientific / engineering applications. Despite being written entirely in python, the library is very fast due to its heavy leverage of numpy for number crunching and Qt's GraphicsView framework for fast display." - http://www.pyqtgraph.org/

PyQtGraph or Matplotlib?

If you just need to make neat publication-quality plots/figures, then Matplotlib should be your first choice. However, if you are interested in making fast plot updates (> 50 updates per sec), then PyQtGraph is probably the best library to use.

Prerequisites for this notebook:

  • Numpy
  • (optional) Basics of PyQt

This notebook covers a few basic features of the library that are sufficient to get you started. The main topics covered here are:

  • Animate data stored in numpy arrays (~ a video).
  • How to style your plots.
  • How to setup a grid layout.

Refer to the examples provided in the package to learn different features of PyQtGraph. These examples can be accessed via a GUI by running the following in a python shell:


In [ ]:
import pyqtgraph.examples
pyqtgraph.examples.run()

Animate Numpy Arrays


In [ ]:
import pyqtgraph as pg             # pg is often used as the shorthand notation
from pyqtgraph.Qt import QtCore    # import QtCore from the Qt library

pyqtgraph.Qt links to the PyQt library. We wish to use the timer() function of the pyqt library in our example. The timer function can be used if you want someething to happen “in a while” or “every once in a while”.


In [ ]:
app = pg.QtGui.QApplication([])    # init QApplication

Here, app refers to an instance of the Qt's QApplication class.

QApplication manages the GUI-application's control flow, where all events from the window system and other sources are processed and dispatched. There can only be one QApplication object defined for all your plots created.


In [ ]:
x = np.random.rand(500,50,50)     # create a random numpy array to display - 500 images of size 50x50

In [ ]:
pg.setConfigOptions(antialias=True)   # enable antialiasing

view = pg.GraphicsView()              # create a main graphics window

view.show()                           # show the window

When displaying images at a different resolution, setting antialias to True makes the graphics appear smooth without any artifacts. Antialiasing minimizes aliasing when representing a high-resolution image at a lower resolution. Other useful config options are 'background' and 'foreground' colors.

GraphicsView generates a main graphics window. The default size is (640,480). You can change this to the size of your choice by using the resize function, e.g, view.resize(50,50).


In [ ]:
p = pg.PlotItem()                   # add a plotItem

view.setCentralItem(p)              # add the plotItem to the graphicsWindow and set it as central

For a given graphics window, you can create multiple plots. Here, we created a single plot item and added it to the graphics window.


In [ ]:
img = pg.ImageItem(border='w', levels=(x.min(),x.max()))  # create an image object

p.addItem(img)                                            # add the imageItem to the plotItem

Within each plot, you can define multiple drawing items (or artists). Here, we added an image item. Examples of other items are: PlotCurveItem, ArrowItem, etc.


In [ ]:
# hide axis and set title
p.hideAxis('left'); p.hideAxis('bottom'); p.hideAxis('top'); p.hideAxis('right')
p.setTitle('Array Animation', size='25px', color='y')

In [ ]:
# data update function
cnt=0
def animLoop():
    global cnt
    if cnt < x.shape[0]:
        img.setImage(x[cnt])
    cnt+=1

Here, we create a function to update the image item with new data. To this end, we use a counter to iterate over each image stored within x.


In [ ]:
# setup and start the timer
timer = QtCore.QTimer()
timer.timeout.connect(animLoop)
timer.start(0)

The timer function is used to repeatedly call the animLoop with a delay of 0 between each call.


In [ ]:
app.exec_()           # execute the app

Finally, you need to execute the QApplication. Any PyQtGraph code must be wrapped between the app initialization and the app execution. Here is the code all put together (execute and check):


In [ ]:
# Animate a 3D numpy array

import numpy as np
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore

app = pg.QtGui.QApplication([])

x = np.random.rand(500,50,50)

pg.setConfigOptions(antialias=True)

# main graphics window
view = pg.GraphicsView()

# show the window
view.show()

# add a plotItem
p = pg.PlotItem()

# add the plotItem to the graphicsWindow and set it as central
view.setCentralItem(p)

# create an image object
img = pg.ImageItem(border='w', levels=(x.min(),x.max()))

# add the imageItem to the plotItem
p.addItem(img)

# hide axis and set title
p.hideAxis('left'); p.hideAxis('bottom'); p.hideAxis('top'); p.hideAxis('right')
p.setTitle('Array Animation', size='25px', color='y')

# data generator
cnt=0
def animLoop():
    global cnt
    if cnt < x.shape[0]:
        img.setImage(x[cnt])
    cnt+=1
    
timer = QtCore.QTimer()
timer.timeout.connect(animLoop)
timer.start(0)

app.exec_()

Exercise 1

  • Animate an RGB array.

  • Animate a 2D array (sequence of line plots). Use pg.PlotCurveItem instead of pg.ImageItem and setData instead of setImage to update the data.

Styling Plots

PyQtGraph provides a function called mkPen(args) to create a drawing pen that can be passed as an argument (pen = pg.mkPen()) to style while defining several plot items. A few examples of defining mkPen are:

  • pg.mkPen('y', width=3, style=QtCore.Qt.DashLine) # Make a dashed yellow line 2px wide
  • pg.mkPen(0.5) # Solid gray line 1px wide
  • pg.mkPen(color=(200,200,255), style=QtCore.Qt.DotLine) # Dotted pale-blue line

Exercise 2

Repeat Exercise 1 with a yellow dashed line plot animation.

Plots Grid Layout

You can create a grid layout for your plots using the GraphicsLayout function. The layout can then be used as a placeholder for all your plots within the main graphics window. Here is an example with two plots placed next to each other beneath a wide text block:


In [ ]:
# imports 
import numpy as np
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore

# init qApp
app = pg.QtGui.QApplication([])

# setup the main window
view = pg.GraphicsView()
view.resize(900,500)
view.setWindowTitle('Notebook')
view.show()

# main layout
layout = pg.GraphicsLayout(border='r')     # with a red bordercolor 

# set the layout as a central item
view.setCentralItem(layout)

# create a text block
label = pg.LabelItem('PyQtGraph Grid Layout Example', size='25px', color='y')

# create a plot with two random curves
p1 = pg.PlotItem()
curve11 = pg.PlotCurveItem(pen=pg.mkPen(color='g', width=1))
curve12 = pg.PlotCurveItem(pen=pg.mkPen(color='b', width=1, style=QtCore.Qt.DashLine))
p1.addItem(curve11); p1.addItem(curve12)
curve11.setData(np.random.rand(100))
curve12.setData(np.random.rand(100))

# create another plot with two random curves
p2 = pg.PlotItem()
curve21 = pg.PlotCurveItem(pen=pg.mkPen(color='w', width=1, style=QtCore.Qt.DotLine))
curve22 = pg.PlotCurveItem(pen=pg.mkPen(color='c', width=1, style=QtCore.Qt.DashLine))
p2.addItem(curve21); p2.addItem(curve22)
curve21.setData(np.random.rand(100))
curve22.setData(np.random.rand(100))

# Finally organize the layout
layout.addItem(label, row=0, col=0, colspan=2)
layout.addItem(p1, row=1, col=0)
layout.addItem(p2, row=1, col=1)

app.exec_()

The above example also shows how to draw multiple curves within the same plot.

Exercise 3

  • Create a grid layout like the example above and animate one of the curves in the left plot.
  • Animate both curves within the left plot.

Summary

In this notebook, we have covered the basics of the PyQtGraph library to make fast animations in Python. We suggest you next to have a look at the main documentation of the library and also the examples provided within the library. Enjoy animating plots!