2

I am working on a project on which I have to plot an image of 320*250 pixels and this 60 times per second if possible, on a window of a GUI. So I try to do this with matplotlib 2.0.2, Python 3.6 and PyQt5 (because I begin to know these tools and work on another project with this), in the following way :

import sys, random, matplotlib
from PyQt5 import QtCore, QtGui, QtWidgets

matplotlib.use('Qt5Agg')
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import matplotlib.pyplot as plt

class SecondWindow(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(SecondWindow, self).__init__(parent)
        self.setupUi(self)

    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.resize(800, 600)

        self.figure = plt.figure()
        self.canvas = FigureCanvas(self.figure)
        self.axes = self.figure.add_subplot(111)

        self.setLayout(QtWidgets.QVBoxLayout())
        self.layout().addWidget(self.canvas)

        self.initialisationFigure()

        self.timer = QtCore.QTimer(self)
        self.timer.timeout.connect(self.majFigure)
        self.timer.start(16)

        self.timer2 = QtCore.QTimer(self)
        self.timer2.timeout.connect(self.NumberRefreshPerSecond)
        self.timer2.start(1000)

    def NumberRefreshPerSecond(self):
        print(self.count)
        self.count = 0

    def majFigure(self):
        self.count = self.count + 1
        self.plot.set_data([[random.random() for x in range(1, 320)] for y in range(1, 250)])
        # self.canvas.draw()
        self.axes.draw_artist(self.axes.patch)
        self.axes.draw_artist(self.plot)
        self.canvas.update()
        self.canvas.flush_events()

    def initialisationFigure(self):
        self.plot = self.axes.imshow([[random.random() for x in range(1,320)] for y in range(1,250)], interpolation='none')
        self.count = 0
        self.canvas.draw()

    def closeEvent(self, event):
        self.timer.stop()

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    form = SecondWindow()
    form.show()
    sys.exit(app.exec_())

I optimized like I can turning off the interpolation, and drawing only once the figure, but with this code the program refresh the figure only 20 times per second whereas the timer is correctly set to 16ms (1/60Hz).

I hope someone can help me giving me some clues to improve my code. I thank you in advance a lot !

Mathieu Gauquelin
  • 601
  • 11
  • 35
  • 1
    Have you had a look at [matplotlib's `animation` module](https://matplotlib.org/api/animation_api.html)? – Paul Brodersen Aug 29 '17 at 09:56
  • Not yet, I will now and come back to give you my new fps. – Mathieu Gauquelin Aug 29 '17 at 10:19
  • FYI, using `numpy.random.random((250, 320))` instead of the loop to create the image gives a boost of 10-15fps on my machine – user3419537 Aug 29 '17 at 10:48
  • 1
    The enlarging of the canvas is also slowing down the plot. Resizing the window from 800x600 to something like 640x480 allows me to hit 60fps – user3419537 Aug 29 '17 at 11:04
  • I increase also the fps like this but at the end, the window will be bigger I believe. But thank you I didn't know the size impact the fps. And for random, it is just an example, at the end i will have data collect by a local network which discuss with a LabVIEW program. – Mathieu Gauquelin Aug 29 '17 at 11:33
  • Ok no doubt animation is a powerful thing. I have now 60 FPS with a window 800x600 (and 25 FPS if full screen 1900x1200) but I don't think I can go faster now. Thanks ! I accept 9dogs answer to close subject. – Mathieu Gauquelin Aug 29 '17 at 14:22

1 Answers1

3

Matplotlib produces publication quality plots but unfortunately it's not quite suitable for real-time plotting and videos.

If it's not a strict requirement, consider using pyqtgraph module. It plays well with pyqt5 and designed to cover shortcomings of matplotlib, especially in real-time area:

If you are doing anything requiring rapid plot updates, video, or realtime interactivity, matplotlib is not the best choice. This is (in my opinion) matplotlib's greatest weakness

(from pyqtgraph site)

It also got additional (optional) features like Region-of-Interest, normalization and histogram plotting.

This code can produce ~160 FPS (with histogram disabled) on my laptop:

import sys, random, matplotlib
from PyQt5 import QtCore, QtGui, QtWidgets

import pyqtgraph as pg
import numpy as np


class SecondWindow(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(SecondWindow, self).__init__(parent)
        self.setupUi(self)

    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.resize(800, 600)

        self.im_widget = pg.ImageView(self)
        # uncomment to hide histogram
        # self.im_widget.ui.histogram.hide()

        self.setLayout(QtWidgets.QVBoxLayout())
        self.layout().addWidget(self.im_widget)

        self.initialisationFigure()

        self.timer = QtCore.QTimer(self)
        self.timer.timeout.connect(self.majFigure)
        self.timer.start(16)

        self.timer2 = QtCore.QTimer(self)
        self.timer2.timeout.connect(self.NumberRefreshPerSecond)
        self.timer2.start(1000)

    def NumberRefreshPerSecond(self):
        print(self.count)
        self.count = 0

    def majFigure(self):
        self.count = self.count + 1
        # numpy random.rand also much faster than list comprehension
        data = np.random.rand(320, 250)
        self.im_widget.setImage(data)

    def initialisationFigure(self):
        self.count = 0
        self.im_widget.show()

    def closeEvent(self, event):
        self.timer.stop()

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    form = SecondWindow()
    form.show()
    sys.exit(app.exec_())
9dogs
  • 1,446
  • 10
  • 18
  • 60 fps on my computer so indeed it is faster. But I don't know pyqtgraph. Is it easy to deal with the figure as matplotlib (labels, zoom, cursor, etc) ? – Mathieu Gauquelin Aug 29 '17 at 10:31
  • 1
    Indeed it is a trade-off between prettiness of matplotlib and speed of pyqtgraph. Although pyqtgraph is highly configurable I've failed to create such beautiful plots like matplotlib's. As for your question - such things as labels, zoom, cursor etc. are totally configurable. I suggest you install pyqtgraph and run its showcase and then decide: `python -m pyqtgraph.examples` – 9dogs Aug 29 '17 at 10:37