Ответ 1
Смотрите это обсуждение: https://groups.google.com/forum/?fromgroups#!searchin/pyqtgraph/arraytoqpath/pyqtgraph/CBLmhlKWnfo/jinNoI07OqkJ
Pyqtgraph не перерисовывает после каждого вызова plot(); он будет ждать, пока элемент управления не вернется в цикл событий Qt до перерисовки. Однако возможно, что ваш код заставляет цикл событий чаще посещаться, вызывая QApplication.processEvents() (это может происходить косвенно, например, если у вас есть диалог прогресса).
Как правило, наиболее важным правилом повышения производительности является: профиль вашего кода. Не делайте предположений о том, что может замедлить вас, если вы можете прямо измерить это.
Так как у меня нет доступа к вашему коду, я могу только догадываться, как его улучшить и показать, как может помочь профилирование. Я собираюсь начать с "медленного" примера здесь и проделать несколько улучшений.
1. Медленная реализация
import pyqtgraph as pg
import numpy as np
app = pg.mkQApp()
data = np.random.normal(size=(120,20000), scale=0.2) + \
np.arange(120)[:,np.newaxis]
view = pg.GraphicsLayoutWidget()
view.show()
w1 = view.addPlot()
now = pg.ptime.time()
for n in data:
w1.plot(n)
print "Plot time: %0.2f sec" % (pg.ptime.time()-now)
app.exec_()
Результат этого:
Plot time: 6.10 sec
Теперь разрешите его профиль:
$ python -m cProfile -s cumulative speed_test.py
. . .
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.001 0.001 11.705 11.705 speed_test.py:1(<module>)
120 0.002 0.000 8.973 0.075 PlotItem.py:614(plot)
120 0.011 0.000 8.521 0.071 PlotItem.py:500(addItem)
363/362 0.030 0.000 7.982 0.022 ViewBox.py:559(updateAutoRange)
. . .
Уже мы видим, что ViewBox.updateAutoRange занимает много времени, поэтому отключите автоматическое определение:
2. Немного быстрее
import pyqtgraph as pg
import numpy as np
app = pg.mkQApp()
data = np.random.normal(size=(120,20000), scale=0.2) + \
np.arange(120)[:,np.newaxis]
view = pg.GraphicsLayoutWidget()
view.show()
w1 = view.addPlot()
w1.disableAutoRange()
now = pg.ptime.time()
for n in data:
w1.plot(n)
w1.autoRange() # only after plots are added
print "Plot time: %0.2f sec" % (pg.ptime.time()-now)
app.exec_()
.. и вывод:
Plot time: 0.68 sec
Итак, это немного быстрее, но панорамирование/масштабирование графика все еще довольно медленное. Если я просмотрю профиль после перетаскивания графика на некоторое время, это выглядит так:
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.034 0.034 16.627 16.627 speed_test.py:1(<module>)
1 1.575 1.575 11.627 11.627 {built-in method exec_}
20 0.000 0.000 7.426 0.371 GraphicsView.py:147(paintEvent)
20 0.124 0.006 7.425 0.371 {paintEvent}
2145 0.076 0.000 6.996 0.003 PlotCurveItem.py:369(paint)
Таким образом, мы видим много вызовов PlotCurveItem.paint(). Что, если мы поместим все 120 сюжетных линий в один элемент, чтобы уменьшить количество вызовов краски?
3. Быстрая реализация
После нескольких раундов профилирования я придумал это. Он основан на использовании pg.arrayToQPath, как предложено в приведенном выше потоке:
import pyqtgraph as pg
import numpy as np
app = pg.mkQApp()
y = np.random.normal(size=(120,20000), scale=0.2) + np.arange(120)[:,np.newaxis]
x = np.empty((120,20000))
x[:] = np.arange(20000)[np.newaxis,:]
view = pg.GraphicsLayoutWidget()
view.show()
w1 = view.addPlot()
class MultiLine(pg.QtGui.QGraphicsPathItem):
def __init__(self, x, y):
"""x and y are 2D arrays of shape (Nplots, Nsamples)"""
connect = np.ones(x.shape, dtype=bool)
connect[:,-1] = 0 # don't draw the segment between each trace
self.path = pg.arrayToQPath(x.flatten(), y.flatten(), connect.flatten())
pg.QtGui.QGraphicsPathItem.__init__(self, self.path)
self.setPen(pg.mkPen('w'))
def shape(self): # override because QGraphicsPathItem.shape is too expensive.
return pg.QtGui.QGraphicsItem.shape(self)
def boundingRect(self):
return self.path.boundingRect()
now = pg.ptime.time()
lines = MultiLine(x, y)
w1.addItem(lines)
print "Plot time: %0.2f sec" % (pg.ptime.time()-now)
app.exec_()
Он запускается быстро, и панорамирование/масштабирование разумно реагируют. Однако я хочу подчеркнуть, что, если это решение будет работать для вас, скорее всего, будет зависеть от деталей вашей программы.