more cleanups
added simple test
This commit is contained in:
parent
b813ecabc3
commit
c9c2160856
@ -228,8 +228,6 @@ class ScatterPlotItem(GraphicsObject):
|
|||||||
GraphicsObject.__init__(self)
|
GraphicsObject.__init__(self)
|
||||||
|
|
||||||
self.picture = None # QPicture used for rendering when pxmode==False
|
self.picture = None # QPicture used for rendering when pxmode==False
|
||||||
self.fragments = None # fragment specification for pxmode; updated every time the view changes.
|
|
||||||
self.target = None
|
|
||||||
self.fragmentAtlas = SymbolAtlas()
|
self.fragmentAtlas = SymbolAtlas()
|
||||||
|
|
||||||
self.data = np.empty(0, dtype=[('x', float), ('y', float), ('size', float), ('symbol', object), ('pen', object), ('brush', object), ('data', object), ('item', object), ('sourceRect', object), ('targetRect', object), ('width', float)])
|
self.data = np.empty(0, dtype=[('x', float), ('y', float), ('size', float), ('symbol', object), ('pen', object), ('brush', object), ('data', object), ('item', object), ('sourceRect', object), ('targetRect', object), ('width', float)])
|
||||||
@ -394,6 +392,7 @@ class ScatterPlotItem(GraphicsObject):
|
|||||||
self.setPointData(kargs['data'], dataSet=newData)
|
self.setPointData(kargs['data'], dataSet=newData)
|
||||||
|
|
||||||
self.prepareGeometryChange()
|
self.prepareGeometryChange()
|
||||||
|
self.informViewBoundsChanged()
|
||||||
self.bounds = [None, None]
|
self.bounds = [None, None]
|
||||||
self.invalidate()
|
self.invalidate()
|
||||||
self.updateSpots(newData)
|
self.updateSpots(newData)
|
||||||
@ -402,13 +401,10 @@ class ScatterPlotItem(GraphicsObject):
|
|||||||
def invalidate(self):
|
def invalidate(self):
|
||||||
## clear any cached drawing state
|
## clear any cached drawing state
|
||||||
self.picture = None
|
self.picture = None
|
||||||
self.fragments = None
|
|
||||||
self.target = None
|
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def getData(self):
|
def getData(self):
|
||||||
return self.data['x'], self.data['y']
|
return self.data['x'], self.data['y']
|
||||||
|
|
||||||
|
|
||||||
def setPoints(self, *args, **kargs):
|
def setPoints(self, *args, **kargs):
|
||||||
##Deprecated; use setData
|
##Deprecated; use setData
|
||||||
@ -554,14 +550,10 @@ class ScatterPlotItem(GraphicsObject):
|
|||||||
sourceRect = self.fragmentAtlas.getSymbolCoords(opts)
|
sourceRect = self.fragmentAtlas.getSymbolCoords(opts)
|
||||||
dataSet['sourceRect'][mask] = sourceRect
|
dataSet['sourceRect'][mask] = sourceRect
|
||||||
|
|
||||||
|
self.fragmentAtlas.getAtlas() # generate atlas so source widths are available.
|
||||||
#for rec in dataSet:
|
|
||||||
#if rec['fragCoords'] is None:
|
|
||||||
#invalidate = True
|
|
||||||
#rec['fragCoords'] = self.fragmentAtlas.getSymbolCoords(*self.getSpotOpts(rec))
|
|
||||||
self.fragmentAtlas.getAtlas()
|
|
||||||
dataSet['width'] = np.array(list(imap(QtCore.QRectF.width, dataSet['sourceRect'])))/2
|
dataSet['width'] = np.array(list(imap(QtCore.QRectF.width, dataSet['sourceRect'])))/2
|
||||||
dataSet['targetRect'] = list(imap(QtCore.QRectF, repeat(0), repeat(0), dataSet['width']*2, dataSet['width']*2))
|
dataSet['targetRect'] = None
|
||||||
self._maxSpotPxWidth = self.fragmentAtlas.max_width
|
self._maxSpotPxWidth = self.fragmentAtlas.max_width
|
||||||
else:
|
else:
|
||||||
self._maxSpotWidth = 0
|
self._maxSpotWidth = 0
|
||||||
@ -684,40 +676,42 @@ class ScatterPlotItem(GraphicsObject):
|
|||||||
self.prepareGeometryChange()
|
self.prepareGeometryChange()
|
||||||
GraphicsObject.viewTransformChanged(self)
|
GraphicsObject.viewTransformChanged(self)
|
||||||
self.bounds = [None, None]
|
self.bounds = [None, None]
|
||||||
self.fragments = None
|
self.data['targetRect'] = None
|
||||||
self.target = None
|
|
||||||
|
|
||||||
def setExportMode(self, *args, **kwds):
|
def setExportMode(self, *args, **kwds):
|
||||||
GraphicsObject.setExportMode(self, *args, **kwds)
|
GraphicsObject.setExportMode(self, *args, **kwds)
|
||||||
self.invalidate()
|
self.invalidate()
|
||||||
|
|
||||||
|
|
||||||
def getTransformedPoint(self):
|
def mapPointsToDevice(self, pts):
|
||||||
# Map point locations to device
|
# Map point locations to device
|
||||||
|
|
||||||
vb = self.getViewBox()
|
|
||||||
if vb is None:
|
|
||||||
return None, None
|
|
||||||
tr = self.deviceTransform()
|
tr = self.deviceTransform()
|
||||||
if tr is None:
|
if tr is None:
|
||||||
return None, None
|
return None
|
||||||
|
|
||||||
pts = np.empty((2,len(self.data['x'])))
|
#pts = np.empty((2,len(self.data['x'])))
|
||||||
pts[0] = self.data['x']
|
#pts[0] = self.data['x']
|
||||||
pts[1] = self.data['y']
|
#pts[1] = self.data['y']
|
||||||
pts = fn.transformCoordinates(tr, pts)
|
pts = fn.transformCoordinates(tr, pts)
|
||||||
pts -= self.data['width']
|
pts -= self.data['width']
|
||||||
pts = np.clip(pts, -2**30, 2**30) ## prevent Qt segmentation fault.
|
pts = np.clip(pts, -2**30, 2**30) ## prevent Qt segmentation fault.
|
||||||
|
|
||||||
## Remove out of view points
|
return pts
|
||||||
|
|
||||||
|
def getViewMask(self, pts):
|
||||||
|
# Return bool mask indicating all points that are within viewbox
|
||||||
|
# pts is expressed in *device coordiantes*
|
||||||
|
vb = self.getViewBox()
|
||||||
|
if vb is None:
|
||||||
|
return None
|
||||||
viewBounds = vb.mapRectToDevice(vb.boundingRect())
|
viewBounds = vb.mapRectToDevice(vb.boundingRect())
|
||||||
w = self.data['width']
|
w = self.data['width']
|
||||||
mask = ((pts[0] + w > viewBounds.left()) &
|
mask = ((pts[0] + w > viewBounds.left()) &
|
||||||
(pts[0] - w < viewBounds.right()) &
|
(pts[0] - w < viewBounds.right()) &
|
||||||
(pts[1] + w > viewBounds.top()) &
|
(pts[1] + w > viewBounds.top()) &
|
||||||
(pts[1] - w < viewBounds.bottom())) ## remove out of view points
|
(pts[1] - w < viewBounds.bottom())) ## remove out of view points
|
||||||
print np.sum(mask)
|
return mask
|
||||||
return self.data[mask], pts[:, mask]
|
|
||||||
|
|
||||||
@debug.warnOnException ## raising an exception here causes crash
|
@debug.warnOnException ## raising an exception here causes crash
|
||||||
def paint(self, p, *args):
|
def paint(self, p, *args):
|
||||||
@ -733,27 +727,42 @@ class ScatterPlotItem(GraphicsObject):
|
|||||||
scale = 1.0
|
scale = 1.0
|
||||||
|
|
||||||
if self.opts['pxMode'] is True:
|
if self.opts['pxMode'] is True:
|
||||||
atlas = self.fragmentAtlas.getAtlas()
|
|
||||||
p.resetTransform()
|
p.resetTransform()
|
||||||
|
|
||||||
data, pts = self.getTransformedPoint()
|
# Map point coordinates to device
|
||||||
if data is None:
|
pts = np.vstack([self.data['x'], self.data['y']])
|
||||||
|
pts = self.mapPointsToDevice(pts)
|
||||||
|
if pts is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Cull points that are outside view
|
||||||
|
viewMask = self.getViewMask(pts)
|
||||||
|
#pts = pts[:,mask]
|
||||||
|
#data = self.data[mask]
|
||||||
|
|
||||||
if self.opts['useCache'] and self._exportOpts is False:
|
if self.opts['useCache'] and self._exportOpts is False:
|
||||||
|
# Draw symbols from pre-rendered atlas
|
||||||
|
atlas = self.fragmentAtlas.getAtlas()
|
||||||
|
|
||||||
if self.target == None:
|
# Update targetRects if necessary
|
||||||
list(imap(QtCore.QRectF.moveTo, data['targetRect'], pts[0,:], pts[1,:]))
|
updateMask = viewMask & np.equal(self.data['targetRect'], None)
|
||||||
self.target = data['targetRect']
|
if np.any(updateMask):
|
||||||
|
updatePts = pts[:,updateMask]
|
||||||
|
width = self.data[updateMask]['width']*2
|
||||||
|
self.data['targetRect'][updateMask] = list(imap(QtCore.QRectF, updatePts[0,:], updatePts[1,:], width, width))
|
||||||
|
|
||||||
|
data = self.data[viewMask]
|
||||||
if USE_PYSIDE:
|
if USE_PYSIDE:
|
||||||
list(imap(p.drawPixmap, self.target, repeat(atlas), data['sourceRect']))
|
list(imap(p.drawPixmap, data['targetRect'], repeat(atlas), data['sourceRect']))
|
||||||
else:
|
else:
|
||||||
p.drawPixmapFragments(self.target.tolist(), data['sourceRect'].tolist(), atlas)
|
p.drawPixmapFragments(data['targetRect'].tolist(), data['sourceRect'].tolist(), atlas)
|
||||||
else:
|
else:
|
||||||
|
# render each symbol individually
|
||||||
p.setRenderHint(p.Antialiasing, aa)
|
p.setRenderHint(p.Antialiasing, aa)
|
||||||
|
|
||||||
for i in range(len(self.data)):
|
data = self.data[viewMask]
|
||||||
rec = data[i]
|
pts = pts[:,viewMask]
|
||||||
|
for i, rec in enumerate(data):
|
||||||
p.resetTransform()
|
p.resetTransform()
|
||||||
p.translate(pts[0,i] + rec['width'], pts[1,i] + rec['width'])
|
p.translate(pts[0,i] + rec['width'], pts[1,i] + rec['width'])
|
||||||
drawSymbol(p, *self.getSpotOpts(rec, scale))
|
drawSymbol(p, *self.getSpotOpts(rec, scale))
|
||||||
|
23
pyqtgraph/graphicsItems/tests/ScatterPlotItem.py
Normal file
23
pyqtgraph/graphicsItems/tests/ScatterPlotItem.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import pyqtgraph as pg
|
||||||
|
import numpy as np
|
||||||
|
app = pg.mkQApp()
|
||||||
|
plot = pg.plot()
|
||||||
|
app.processEvents()
|
||||||
|
|
||||||
|
# set view range equal to its bounding rect.
|
||||||
|
# This causes plots to look the same regardless of pxMode.
|
||||||
|
plot.setRange(rect=plot.boundingRect())
|
||||||
|
|
||||||
|
|
||||||
|
def test_modes():
|
||||||
|
for i, pxMode in enumerate([True, False]):
|
||||||
|
for j, useCache in enumerate([True, False]):
|
||||||
|
s = pg.ScatterPlotItem()
|
||||||
|
s.opts['useCache'] = useCache
|
||||||
|
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.addPoints(x=np.array([60, 70])+i*100, y=np.array([60, 70])+j*100, size=[20, 30])
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
test_modes()
|
Loading…
Reference in New Issue
Block a user