more cleanups

added simple test
This commit is contained in:
Luke Campagnola 2014-01-18 19:13:39 -05:00
parent b813ecabc3
commit c9c2160856
2 changed files with 70 additions and 38 deletions

View File

@ -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))

View 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()