I'm trying to convert some code from using matplotlib to pyqtgraph since it's supposed to be much more efficient, and it provides a lot more interactive features. I've got the code mostly converted, but I'm running into issues where it just runs slowly. Some code that reproduces this:
import numpy as np
import qtpy.QtWidgets as qt
import pyqtgraph as pg
class GraphWidget(qt.QWidget):
"""A widget for simplifying graphing tasks
:param qt.QWidget parent:
:param Dict[str, dict] layout: A mapping from title to row/col/rowspan/colspan kwargs
"""
def __init__(self, parent, layout_spec):
super(GraphWidget, self).__init__(parent=parent)
self.axes = {}
glw = pg.GraphicsLayoutWidget(parent=self)
for name, layout in layout_spec.items():
self.axes[name] = pg.PlotItem(name=name, title=name)
glw.addItem(self.axes[name], **layout)
box_layout = qt.QVBoxLayout()
box_layout.addWidget(glw, 1)
self.setLayout(box_layout)
@property
def normal_pen(self):
return pg.mkPen(color='w', width=2)
@property
def good_pen(self):
return pg.mkPen(color='g', width=2)
@property
def bad_pen(self):
return pg.mkPen(color='r', width=2)
def plot(self, mode, x, y, axis):
if mode == 'normal':
pen = self.normal_pen
elif mode == 'good':
pen = self.good_pen
elif mode == 'bad':
pen = self.bad_pen
plot_item = pg.PlotCurveItem(x, y, pen=pen)
self.axes[axis].addItem(plot_item)
if __name__ == '__main__':
import random
import time
# qt.QApplication.setGraphicsSystem('opengl')
app = qt.QApplication([])
window = qt.QWidget(parent=None)
layout = qt.QVBoxLayout()
gw = GraphWidget(
window,
{
'A': dict(row=1, col=1, rowspan=2),
'B': dict(row=1, col=2),
'C': dict(row=2, col=2),
'D': dict(row=1, col=3),
'E': dict(row=2, col=3),
'F': dict(row=1, col=4),
'G': dict(row=2, col=4),
}
)
layout.addWidget(gw, 1)
def plot():
start = time.time()
for axis in 'ABCDEFG':
gw.plot(
random.choice(['normal', 'good', 'bad']),
np.arange(2000),
np.random.rand(2000),
axis,
)
# necessary because without it, the "plotting" completes in ms,
# but the UI doesn't update for a while still
app.processEvents()
print('Plotting time: {}'.format(time.time() - start))
button = qt.QPushButton(parent=window, text='Plot')
button.pressed.connect(plot)
layout.addWidget(button)
window.setLayout(layout)
window.showMaximized()
app.exec_()
The number and layout of plots, and the number of points, is chosen to reflect the real-world use case I have. If I run this as is and click the plot button twice I see
Plotting time: 3.61599993706
Plotting time: 7.04699993134
I stopped at this point because the application in general was suddenly very slow and took several seconds just to close. If I uncomment the one line to enable opengl rendering, I can easily run it more times and it looks like
Plotting time: 0.0520000457764
Plotting time: 0.328999996185
Plotting time: 0.453000068665
Plotting time: 0.55999994278
Plotting time: 0.674000024796
Plotting time: 1.21900010109
Plotting time: 0.936000108719
Plotting time: 1.06100010872
Plotting time: 1.19899988174
Plotting time: 1.35100007057
At this point I can also tell that the times reported here are not really accurate, it takes longer than these times for the UI to actually reflect the update.
This is a fairly typical number of graphs for me to be able to deal with, this application could easily see 16 sets of graphs (one additional line per axis) in about 20 seconds. If my UI is getting progressively slower this just isn't sufficiently fast.
Down-sampling doesn't seem to apply with the PlotCurveItem (unlike PlotDataItem), but I did discover that doing
plot_item = pg.PlotCurveItem(x, y, pen=pen, connect='pairs')
results in much faster times:
Plotting time: 0.0520000457764
Plotting time: 0.0900001525879
Plotting time: 0.138000011444
Plotting time: 0.108000040054
Plotting time: 0.117000102997
Plotting time: 0.12299990654
Plotting time: 0.143000125885
Plotting time: 0.15499997139
This still seems pretty slow to me though, and I'm wondering if there's anything that can be done to speed it up further. It's also still very slow (on the order of several seconds per plot) if I don't set it to opengl. I'm really looking for as fast of plotting as I can possibly manage. My target would be <100ms per plot.
It's hard to tell where to slowdowns really are though, profiling this code seems like it will be difficult since some of it happens at the OpenGL level, some deep inside Qt code, and some in pyqtgraph itself.
Is there any other way to speed up this code further?
Note: I am currently using Python 2.7 64bit, PyQt 4.10.4, and pyqtgraph 0.10.0 installed via conda, although this code will need to work equally well on Python 3.5+.