commit
1f9acd1502
|
@ -11,6 +11,7 @@ import initExample
|
||||||
from pyqtgraph.Qt import QtGui, QtCore
|
from pyqtgraph.Qt import QtGui, QtCore
|
||||||
import pyqtgraph as pg
|
import pyqtgraph as pg
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
from collections import namedtuple
|
||||||
|
|
||||||
app = QtGui.QApplication([])
|
app = QtGui.QApplication([])
|
||||||
mw = QtGui.QMainWindow()
|
mw = QtGui.QMainWindow()
|
||||||
|
@ -32,8 +33,8 @@ print("Generating data, this takes a few seconds...")
|
||||||
## There are a few different ways we can draw scatter plots; each is optimized for different types of data:
|
## There are a few different ways we can draw scatter plots; each is optimized for different types of data:
|
||||||
|
|
||||||
|
|
||||||
## 1) All spots identical and transform-invariant (top-left plot).
|
## 1) All spots identical and transform-invariant (top-left plot).
|
||||||
## In this case we can get a huge performance boost by pre-rendering the spot
|
## In this case we can get a huge performance boost by pre-rendering the spot
|
||||||
## image and just drawing that image repeatedly.
|
## image and just drawing that image repeatedly.
|
||||||
|
|
||||||
n = 300
|
n = 300
|
||||||
|
@ -57,21 +58,41 @@ s1.sigClicked.connect(clicked)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 2) Spots are transform-invariant, but not identical (top-right plot).
|
## 2) Spots are transform-invariant, but not identical (top-right plot).
|
||||||
## In this case, drawing is almsot as fast as 1), but there is more startup
|
## In this case, drawing is almsot as fast as 1), but there is more startup
|
||||||
## overhead and memory usage since each spot generates its own pre-rendered
|
## overhead and memory usage since each spot generates its own pre-rendered
|
||||||
## image.
|
## image.
|
||||||
|
|
||||||
|
TextSymbol = namedtuple("TextSymbol", "label symbol scale")
|
||||||
|
|
||||||
|
def createLabel(label, angle):
|
||||||
|
symbol = QtGui.QPainterPath()
|
||||||
|
#symbol.addText(0, 0, QFont("San Serif", 10), label)
|
||||||
|
f = QtGui.QFont()
|
||||||
|
f.setPointSize(10)
|
||||||
|
symbol.addText(0, 0, f, label)
|
||||||
|
br = symbol.boundingRect()
|
||||||
|
scale = min(1. / br.width(), 1. / br.height())
|
||||||
|
tr = QtGui.QTransform()
|
||||||
|
tr.scale(scale, scale)
|
||||||
|
tr.rotate(angle)
|
||||||
|
tr.translate(-br.x() - br.width()/2., -br.y() - br.height()/2.)
|
||||||
|
return TextSymbol(label, tr.map(symbol), 0.1 / scale)
|
||||||
|
|
||||||
|
random_str = lambda : (''.join([chr(np.random.randint(ord('A'),ord('z'))) for i in range(np.random.randint(1,5))]), np.random.randint(0, 360))
|
||||||
|
|
||||||
s2 = pg.ScatterPlotItem(size=10, pen=pg.mkPen('w'), pxMode=True)
|
s2 = pg.ScatterPlotItem(size=10, pen=pg.mkPen('w'), pxMode=True)
|
||||||
pos = np.random.normal(size=(2,n), scale=1e-5)
|
pos = np.random.normal(size=(2,n), scale=1e-5)
|
||||||
spots = [{'pos': pos[:,i], 'data': 1, 'brush':pg.intColor(i, n), 'symbol': i%5, 'size': 5+i/10.} for i in range(n)]
|
spots = [{'pos': pos[:,i], 'data': 1, 'brush':pg.intColor(i, n), 'symbol': i%5, 'size': 5+i/10.} for i in range(n)]
|
||||||
s2.addPoints(spots)
|
s2.addPoints(spots)
|
||||||
|
spots = [{'pos': pos[:,i], 'data': 1, 'brush':pg.intColor(i, n), 'symbol': label[1], 'size': label[2]*(5+i/10.)} for (i, label) in [(i, createLabel(*random_str())) for i in range(n)]]
|
||||||
|
s2.addPoints(spots)
|
||||||
w2.addItem(s2)
|
w2.addItem(s2)
|
||||||
s2.sigClicked.connect(clicked)
|
s2.sigClicked.connect(clicked)
|
||||||
|
|
||||||
|
|
||||||
## 3) Spots are not transform-invariant, not identical (bottom-left).
|
## 3) Spots are not transform-invariant, not identical (bottom-left).
|
||||||
## This is the slowest case, since all spots must be completely re-drawn
|
## This is the slowest case, since all spots must be completely re-drawn
|
||||||
## every time because their apparent transformation may have changed.
|
## every time because their apparent transformation may have changed.
|
||||||
|
|
||||||
s3 = pg.ScatterPlotItem(pxMode=False) ## Set pxMode=False to allow spots to transform with the view
|
s3 = pg.ScatterPlotItem(pxMode=False) ## Set pxMode=False to allow spots to transform with the view
|
||||||
|
|
|
@ -126,7 +126,7 @@ class SymbolAtlas(object):
|
||||||
keyi = None
|
keyi = None
|
||||||
sourceRecti = None
|
sourceRecti = None
|
||||||
for i, rec in enumerate(opts):
|
for i, rec in enumerate(opts):
|
||||||
key = (rec[3], rec[2], id(rec[4]), id(rec[5])) # TODO: use string indexes?
|
key = (id(rec[3]), rec[2], id(rec[4]), id(rec[5])) # TODO: use string indexes?
|
||||||
if key == keyi:
|
if key == keyi:
|
||||||
sourceRect[i] = sourceRecti
|
sourceRect[i] = sourceRecti
|
||||||
else:
|
else:
|
||||||
|
@ -136,6 +136,7 @@ class SymbolAtlas(object):
|
||||||
newRectSrc = QtCore.QRectF()
|
newRectSrc = QtCore.QRectF()
|
||||||
newRectSrc.pen = rec['pen']
|
newRectSrc.pen = rec['pen']
|
||||||
newRectSrc.brush = rec['brush']
|
newRectSrc.brush = rec['brush']
|
||||||
|
newRectSrc.symbol = rec[3]
|
||||||
self.symbolMap[key] = newRectSrc
|
self.symbolMap[key] = newRectSrc
|
||||||
self.atlasValid = False
|
self.atlasValid = False
|
||||||
sourceRect[i] = newRectSrc
|
sourceRect[i] = newRectSrc
|
||||||
|
@ -151,7 +152,7 @@ class SymbolAtlas(object):
|
||||||
images = []
|
images = []
|
||||||
for key, sourceRect in self.symbolMap.items():
|
for key, sourceRect in self.symbolMap.items():
|
||||||
if sourceRect.width() == 0:
|
if sourceRect.width() == 0:
|
||||||
img = renderSymbol(key[0], key[1], sourceRect.pen, sourceRect.brush)
|
img = renderSymbol(sourceRect.symbol, key[1], sourceRect.pen, sourceRect.brush)
|
||||||
images.append(img) ## we only need this to prevent the images being garbage collected immediately
|
images.append(img) ## we only need this to prevent the images being garbage collected immediately
|
||||||
arr = fn.imageToArray(img, copy=False, transpose=False)
|
arr = fn.imageToArray(img, copy=False, transpose=False)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
from pyqtgraph.Qt import QtGui, QtCore
|
||||||
import pyqtgraph as pg
|
import pyqtgraph as pg
|
||||||
import numpy as np
|
import numpy as np
|
||||||
app = pg.mkQApp()
|
app = pg.mkQApp()
|
||||||
|
@ -7,9 +8,16 @@ app.processEvents()
|
||||||
|
|
||||||
def test_scatterplotitem():
|
def test_scatterplotitem():
|
||||||
plot = pg.PlotWidget()
|
plot = pg.PlotWidget()
|
||||||
# set view range equal to its bounding rect.
|
# set view range equal to its bounding rect.
|
||||||
# This causes plots to look the same regardless of pxMode.
|
# This causes plots to look the same regardless of pxMode.
|
||||||
plot.setRange(rect=plot.boundingRect())
|
plot.setRange(rect=plot.boundingRect())
|
||||||
|
|
||||||
|
# test SymbolAtlas accepts custom symbol
|
||||||
|
s = pg.ScatterPlotItem()
|
||||||
|
symbol = QtGui.QPainterPath()
|
||||||
|
symbol.addEllipse(QtCore.QRectF(-0.5, -0.5, 1, 1))
|
||||||
|
s.addPoints([{'pos': [0,0], 'data': 1, 'symbol': symbol}])
|
||||||
|
|
||||||
for i, pxMode in enumerate([True, False]):
|
for i, pxMode in enumerate([True, False]):
|
||||||
for j, useCache in enumerate([True, False]):
|
for j, useCache in enumerate([True, False]):
|
||||||
s = pg.ScatterPlotItem()
|
s = pg.ScatterPlotItem()
|
||||||
|
@ -17,14 +25,14 @@ def test_scatterplotitem():
|
||||||
plot.addItem(s)
|
plot.addItem(s)
|
||||||
s.setData(x=np.array([10,40,20,30])+i*100, y=np.array([40,60,10,30])+j*100, pxMode=pxMode)
|
s.setData(x=np.array([10,40,20,30])+i*100, y=np.array([40,60,10,30])+j*100, pxMode=pxMode)
|
||||||
s.addPoints(x=np.array([60, 70])+i*100, y=np.array([60, 70])+j*100, size=[20, 30])
|
s.addPoints(x=np.array([60, 70])+i*100, y=np.array([60, 70])+j*100, size=[20, 30])
|
||||||
|
|
||||||
# Test uniform spot updates
|
# Test uniform spot updates
|
||||||
s.setSize(10)
|
s.setSize(10)
|
||||||
s.setBrush('r')
|
s.setBrush('r')
|
||||||
s.setPen('g')
|
s.setPen('g')
|
||||||
s.setSymbol('+')
|
s.setSymbol('+')
|
||||||
app.processEvents()
|
app.processEvents()
|
||||||
|
|
||||||
# Test list spot updates
|
# Test list spot updates
|
||||||
s.setSize([10] * 6)
|
s.setSize([10] * 6)
|
||||||
s.setBrush([pg.mkBrush('r')] * 6)
|
s.setBrush([pg.mkBrush('r')] * 6)
|
||||||
|
@ -55,7 +63,7 @@ def test_scatterplotitem():
|
||||||
|
|
||||||
def test_init_spots():
|
def test_init_spots():
|
||||||
plot = pg.PlotWidget()
|
plot = pg.PlotWidget()
|
||||||
# set view range equal to its bounding rect.
|
# set view range equal to its bounding rect.
|
||||||
# This causes plots to look the same regardless of pxMode.
|
# This causes plots to look the same regardless of pxMode.
|
||||||
plot.setRange(rect=plot.boundingRect())
|
plot.setRange(rect=plot.boundingRect())
|
||||||
spots = [
|
spots = [
|
||||||
|
@ -63,28 +71,28 @@ def test_init_spots():
|
||||||
{'pos': (1, 2), 'pen': None, 'brush': None, 'data': 'zzz'},
|
{'pos': (1, 2), 'pen': None, 'brush': None, 'data': 'zzz'},
|
||||||
]
|
]
|
||||||
s = pg.ScatterPlotItem(spots=spots)
|
s = pg.ScatterPlotItem(spots=spots)
|
||||||
|
|
||||||
# Check we can display without errors
|
# Check we can display without errors
|
||||||
plot.addItem(s)
|
plot.addItem(s)
|
||||||
app.processEvents()
|
app.processEvents()
|
||||||
plot.clear()
|
plot.clear()
|
||||||
|
|
||||||
# check data is correct
|
# check data is correct
|
||||||
spots = s.points()
|
spots = s.points()
|
||||||
|
|
||||||
defPen = pg.mkPen(pg.getConfigOption('foreground'))
|
defPen = pg.mkPen(pg.getConfigOption('foreground'))
|
||||||
|
|
||||||
assert spots[0].pos().x() == 0
|
assert spots[0].pos().x() == 0
|
||||||
assert spots[0].pos().y() == 1
|
assert spots[0].pos().y() == 1
|
||||||
assert spots[0].pen() == defPen
|
assert spots[0].pen() == defPen
|
||||||
assert spots[0].data() is None
|
assert spots[0].data() is None
|
||||||
|
|
||||||
assert spots[1].pos().x() == 1
|
assert spots[1].pos().x() == 1
|
||||||
assert spots[1].pos().y() == 2
|
assert spots[1].pos().y() == 2
|
||||||
assert spots[1].pen() == pg.mkPen(None)
|
assert spots[1].pen() == pg.mkPen(None)
|
||||||
assert spots[1].brush() == pg.mkBrush(None)
|
assert spots[1].brush() == pg.mkBrush(None)
|
||||||
assert spots[1].data() == 'zzz'
|
assert spots[1].data() == 'zzz'
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
test_scatterplotitem()
|
test_scatterplotitem()
|
||||||
|
|
Loading…
Reference in New Issue
Block a user