Vai a questa discussione: https://groups.google.com/forum/?fromgroups#!searchin/pyqtgraph/arraytoqpath/pyqtgraph/CBLmhlKWnfo/jinNoI07OqkJ
Pyqtgraph non ridisegna dopo ogni chiamata per tracciare(); attenderà fino a quando il controllo ritorna al ciclo di eventi Qt prima di ridisegnare. Tuttavia, è possibile che il codice imponga che il ciclo di eventi venga visitato più frequentemente chiamando QApplication.processEvents() (ciò può accadere indirettamente, ad esempio se si dispone di una finestra di dialogo di avanzamento).
In genere, la regola più importante sul miglioramento delle prestazioni è: profile your code. Non fare supposizioni su cosa potrebbe rallentarti se puoi invece misurarlo direttamente.
Poiché non ho accesso al tuo codice, posso solo immaginare come migliorarlo e mostrarti come la profilatura può aiutare. Ho intenzione di iniziare con l'esempio "lento" qui e lavorare con alcuni miglioramenti.
1. La lenta attuazione
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_()
L'output di questo è:
Plot time: 6.10 sec
Ora diamo il profilo it:
$ 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)
. . .
Già possiamo vedere che Viewbox. updateAutoRange impiega molto tempo, quindi disabilitiamo l'auto-ranging:
2. Un po 'più veloce
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_()
..e l'output è:
Plot time: 0.68 sec
Ecco, questo è un po' più veloce, ma panning/scalare la trama è ancora piuttosto lento.Se guardo il profilo dopo aver trascinato la trama per un po ', sembra che questo:
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)
Così vediamo un sacco di chiamate a PlotCurveItem.paint(). Cosa succede se mettiamo tutte le 120 linee di trama in un singolo oggetto per ridurre il numero di chiamate di vernice?
3. La rapida implementazione
Dopo un paio di giri di profiling, sono arrivato fino a questo. Si basa sull'utilizzo di pg.arrayToQPath, come suggerito nel thread di cui sopra:
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_()
Si avvia rapidamente e panning/scala è abbastanza reattivo. Sottolineerò, tuttavia, che se questa soluzione funziona per te probabilmente dipenderà dai dettagli del tuo programma.
Quanto tempo consideri "abbastanza lungo"? Traccio facilmente 20.000 x 120 punti in un secondo. Per un'istantanea non è un problema. Per esempio, voglio mostrare un ECG live a 128 derivazioni, non è abbastanza. – Micke
Anche se riduco i dati a 200x120 punti, sono necessari 6 secondi. Usi lo stesso codice? –
Dato che non mi hai detto esattamente quali sono i "dati", non ne sono sicuro. Ho usato una matrice numpy.empty ([120, 20000], dtype = numpy.int16). Posso postare codice stasera. – Micke