optimize ScatterPlotItem with pxMode=True
This commit is contained in:
parent
029282bb9d
commit
63bf2b3270
@ -48,8 +48,8 @@ else:
|
|||||||
CONFIG_OPTIONS = {
|
CONFIG_OPTIONS = {
|
||||||
'useOpenGL': useOpenGL, ## by default, this is platform-dependent (see widgets/GraphicsView). Set to True or False to explicitly enable/disable opengl.
|
'useOpenGL': useOpenGL, ## by default, this is platform-dependent (see widgets/GraphicsView). Set to True or False to explicitly enable/disable opengl.
|
||||||
'leftButtonPan': True, ## if false, left button drags a rubber band for zooming in viewbox
|
'leftButtonPan': True, ## if false, left button drags a rubber band for zooming in viewbox
|
||||||
'foreground': (150, 150, 150), ## default foreground color for axes, labels, etc.
|
'foreground': 'd', ## default foreground color for axes, labels, etc.
|
||||||
'background': (0, 0, 0), ## default background for GraphicsWidget
|
'background': 'k', ## default background for GraphicsWidget
|
||||||
'antialias': False,
|
'antialias': False,
|
||||||
'editorCommand': None, ## command used to invoke code editor from ConsoleWidgets
|
'editorCommand': None, ## command used to invoke code editor from ConsoleWidgets
|
||||||
'useWeave': True, ## Use weave to speed up some operations, if it is available
|
'useWeave': True, ## Use weave to speed up some operations, if it is available
|
||||||
|
@ -7,15 +7,19 @@ Distributed under MIT/X11 license. See license.txt for more infomation.
|
|||||||
|
|
||||||
from __future__ import division
|
from __future__ import division
|
||||||
from .python2_3 import asUnicode
|
from .python2_3 import asUnicode
|
||||||
|
from .Qt import QtGui, QtCore, USE_PYSIDE
|
||||||
Colors = {
|
Colors = {
|
||||||
'b': (0,0,255,255),
|
'b': QtGui.QColor(0,0,255,255),
|
||||||
'g': (0,255,0,255),
|
'g': QtGui.QColor(0,255,0,255),
|
||||||
'r': (255,0,0,255),
|
'r': QtGui.QColor(255,0,0,255),
|
||||||
'c': (0,255,255,255),
|
'c': QtGui.QColor(0,255,255,255),
|
||||||
'm': (255,0,255,255),
|
'm': QtGui.QColor(255,0,255,255),
|
||||||
'y': (255,255,0,255),
|
'y': QtGui.QColor(255,255,0,255),
|
||||||
'k': (0,0,0,255),
|
'k': QtGui.QColor(0,0,0,255),
|
||||||
'w': (255,255,255,255),
|
'w': QtGui.QColor(255,255,255,255),
|
||||||
|
'd': QtGui.QColor(150,150,150,255),
|
||||||
|
'l': QtGui.QColor(200,200,200,255),
|
||||||
|
's': QtGui.QColor(100,100,150,255),
|
||||||
}
|
}
|
||||||
|
|
||||||
SI_PREFIXES = asUnicode('yzafpnµm kMGTPEZY')
|
SI_PREFIXES = asUnicode('yzafpnµm kMGTPEZY')
|
||||||
@ -23,7 +27,6 @@ SI_PREFIXES_ASCII = 'yzafpnum kMGTPEZY'
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
from .Qt import QtGui, QtCore, USE_PYSIDE
|
|
||||||
import pyqtgraph as pg
|
import pyqtgraph as pg
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import decimal, re
|
import decimal, re
|
||||||
@ -169,17 +172,15 @@ def mkColor(*args):
|
|||||||
"""
|
"""
|
||||||
err = 'Not sure how to make a color from "%s"' % str(args)
|
err = 'Not sure how to make a color from "%s"' % str(args)
|
||||||
if len(args) == 1:
|
if len(args) == 1:
|
||||||
if isinstance(args[0], QtGui.QColor):
|
if isinstance(args[0], basestring):
|
||||||
return QtGui.QColor(args[0])
|
|
||||||
elif isinstance(args[0], float):
|
|
||||||
r = g = b = int(args[0] * 255)
|
|
||||||
a = 255
|
|
||||||
elif isinstance(args[0], basestring):
|
|
||||||
c = args[0]
|
c = args[0]
|
||||||
if c[0] == '#':
|
if c[0] == '#':
|
||||||
c = c[1:]
|
c = c[1:]
|
||||||
if len(c) == 1:
|
if len(c) == 1:
|
||||||
(r, g, b, a) = Colors[c]
|
try:
|
||||||
|
return Colors[c]
|
||||||
|
except KeyError:
|
||||||
|
raise Exception(err)
|
||||||
if len(c) == 3:
|
if len(c) == 3:
|
||||||
r = int(c[0]*2, 16)
|
r = int(c[0]*2, 16)
|
||||||
g = int(c[1]*2, 16)
|
g = int(c[1]*2, 16)
|
||||||
@ -200,6 +201,11 @@ def mkColor(*args):
|
|||||||
g = int(c[2:4], 16)
|
g = int(c[2:4], 16)
|
||||||
b = int(c[4:6], 16)
|
b = int(c[4:6], 16)
|
||||||
a = int(c[6:8], 16)
|
a = int(c[6:8], 16)
|
||||||
|
elif isinstance(args[0], QtGui.QColor):
|
||||||
|
return QtGui.QColor(args[0])
|
||||||
|
elif isinstance(args[0], float):
|
||||||
|
r = g = b = int(args[0] * 255)
|
||||||
|
a = 255
|
||||||
elif hasattr(args[0], '__len__'):
|
elif hasattr(args[0], '__len__'):
|
||||||
if len(args[0]) == 3:
|
if len(args[0]) == 3:
|
||||||
(r, g, b) = args[0]
|
(r, g, b) = args[0]
|
||||||
@ -283,7 +289,7 @@ def mkPen(*args, **kargs):
|
|||||||
color = args
|
color = args
|
||||||
|
|
||||||
if color is None:
|
if color is None:
|
||||||
color = mkColor(200, 200, 200)
|
color = mkColor('l')
|
||||||
if hsv is not None:
|
if hsv is not None:
|
||||||
color = hsvColor(*hsv)
|
color = hsvColor(*hsv)
|
||||||
else:
|
else:
|
||||||
|
@ -266,8 +266,7 @@ class AxisItem(GraphicsWidget):
|
|||||||
self.picture = None
|
self.picture = None
|
||||||
|
|
||||||
def pen(self):
|
def pen(self):
|
||||||
if self._pen is None:
|
#return self._pen
|
||||||
return fn.mkPen(pg.getConfigOption('foreground'))
|
|
||||||
return pg.mkPen(self._pen)
|
return pg.mkPen(self._pen)
|
||||||
|
|
||||||
def setPen(self, pen):
|
def setPen(self, pen):
|
||||||
@ -276,11 +275,11 @@ class AxisItem(GraphicsWidget):
|
|||||||
if pen == None, the default will be used (see :func:`setConfigOption
|
if pen == None, the default will be used (see :func:`setConfigOption
|
||||||
<pyqtgraph.setConfigOption>`)
|
<pyqtgraph.setConfigOption>`)
|
||||||
"""
|
"""
|
||||||
self._pen = pen
|
|
||||||
self.picture = None
|
self.picture = None
|
||||||
if pen is None:
|
if pen is None:
|
||||||
pen = pg.getConfigOption('foreground')
|
pen = pg.getConfigOption('foreground')
|
||||||
self.labelStyle['color'] = '#' + pg.colorStr(pg.mkPen(pen).color())[:6]
|
self._pen = pg.mkPen(pen)
|
||||||
|
self.labelStyle['color'] = '#' + pg.colorStr(self._pen.color())[:6]
|
||||||
self.setLabel()
|
self.setLabel()
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
|
@ -98,31 +98,41 @@ class SymbolAtlas(object):
|
|||||||
# weak value; if all external refs to this list disappear,
|
# weak value; if all external refs to this list disappear,
|
||||||
# the symbol will be forgotten.
|
# the symbol will be forgotten.
|
||||||
self.symbolMap = weakref.WeakValueDictionary()
|
self.symbolMap = weakref.WeakValueDictionary()
|
||||||
|
self.symbolPen = weakref.WeakValueDictionary()
|
||||||
|
self.symbolBrush = weakref.WeakValueDictionary()
|
||||||
|
|
||||||
self.atlasData = None # numpy array of atlas image
|
self.atlasData = None # numpy array of atlas image
|
||||||
self.atlas = None # atlas as QPixmap
|
self.atlas = None # atlas as QPixmap
|
||||||
self.atlasValid = False
|
self.atlasValid = False
|
||||||
|
self.max_width=0
|
||||||
|
|
||||||
def getSymbolCoords(self, opts):
|
def getSymbolCoords(self, opts):
|
||||||
"""
|
"""
|
||||||
Given a list of spot records, return an object representing the coordinates of that symbol within the atlas
|
Given a list of spot records, return an object representing the coordinates of that symbol within the atlas
|
||||||
"""
|
"""
|
||||||
coords = np.empty(len(opts), dtype=object)
|
coords = np.empty(len(opts), dtype=object)
|
||||||
|
keyi = None
|
||||||
|
coordi = None
|
||||||
for i, rec in enumerate(opts):
|
for i, rec in enumerate(opts):
|
||||||
symbol, size, pen, brush = rec['symbol'], rec['size'], rec['pen'], rec['brush']
|
key = (rec[3], rec[2], id(rec[4]), id(rec[5]))
|
||||||
pen = fn.mkPen(pen) if not isinstance(pen, QtGui.QPen) else pen
|
if key == keyi:
|
||||||
brush = fn.mkBrush(brush) if not isinstance(pen, QtGui.QBrush) else brush
|
coords[i]=coordi
|
||||||
key = (symbol, size, fn.colorTuple(pen.color()), pen.widthF(), pen.style(), fn.colorTuple(brush.color()))
|
else:
|
||||||
if key not in self.symbolMap:
|
try:
|
||||||
newCoords = SymbolAtlas.SymbolCoords()
|
coords[i] = self.symbolMap[key]
|
||||||
self.symbolMap[key] = newCoords
|
except KeyError:
|
||||||
self.atlasValid = False
|
newCoords = SymbolAtlas.SymbolCoords()
|
||||||
#try:
|
self.symbolMap[key] = newCoords
|
||||||
#self.addToAtlas(key) ## squeeze this into the atlas if there is room
|
self.symbolPen[key] = rec['pen']
|
||||||
#except:
|
self.symbolBrush[key] = rec['brush']
|
||||||
#self.buildAtlas() ## otherwise, we need to rebuild
|
self.atlasValid = False
|
||||||
|
#try:
|
||||||
coords[i] = self.symbolMap[key]
|
#self.addToAtlas(key) ## squeeze this into the atlas if there is room
|
||||||
|
#except:
|
||||||
|
#self.buildAtlas() ## otherwise, we need to rebuild
|
||||||
|
coords[i] = newCoords
|
||||||
|
keyi = key
|
||||||
|
coordi = newCoords
|
||||||
return coords
|
return coords
|
||||||
|
|
||||||
def buildAtlas(self):
|
def buildAtlas(self):
|
||||||
@ -133,8 +143,8 @@ class SymbolAtlas(object):
|
|||||||
images = []
|
images = []
|
||||||
for key, coords in self.symbolMap.items():
|
for key, coords in self.symbolMap.items():
|
||||||
if len(coords) == 0:
|
if len(coords) == 0:
|
||||||
pen = fn.mkPen(color=key[2], width=key[3], style=key[4])
|
pen = self.symbolPen[key]
|
||||||
brush = fn.mkBrush(color=key[5])
|
brush = self.symbolBrush[key]
|
||||||
img = renderSymbol(key[0], key[1], pen, brush)
|
img = renderSymbol(key[0], key[1], pen, 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)
|
||||||
@ -181,6 +191,7 @@ class SymbolAtlas(object):
|
|||||||
self.atlasData[x:x+w, y:y+h] = rendered[key]
|
self.atlasData[x:x+w, y:y+h] = rendered[key]
|
||||||
self.atlas = None
|
self.atlas = None
|
||||||
self.atlasValid = True
|
self.atlasValid = True
|
||||||
|
self.max_width=maxWidth
|
||||||
|
|
||||||
def getAtlas(self):
|
def getAtlas(self):
|
||||||
if not self.atlasValid:
|
if not self.atlasValid:
|
||||||
@ -237,8 +248,8 @@ class ScatterPlotItem(GraphicsObject):
|
|||||||
'antialias': pg.getConfigOption('antialias'),
|
'antialias': pg.getConfigOption('antialias'),
|
||||||
}
|
}
|
||||||
|
|
||||||
self.setPen(200,200,200, update=False)
|
self.setPen('l', update=False)
|
||||||
self.setBrush(100,100,150, update=False)
|
self.setBrush('s', update=False)
|
||||||
self.setSymbol('o', update=False)
|
self.setSymbol('o', update=False)
|
||||||
self.setSize(7, update=False)
|
self.setSize(7, update=False)
|
||||||
prof.mark('1')
|
prof.mark('1')
|
||||||
@ -533,10 +544,8 @@ class ScatterPlotItem(GraphicsObject):
|
|||||||
def updateSpots(self, dataSet=None):
|
def updateSpots(self, dataSet=None):
|
||||||
if dataSet is None:
|
if dataSet is None:
|
||||||
dataSet = self.data
|
dataSet = self.data
|
||||||
self._maxSpotWidth = 0
|
|
||||||
self._maxSpotPxWidth = 0
|
|
||||||
invalidate = False
|
invalidate = False
|
||||||
self.measureSpotSizes(dataSet)
|
|
||||||
if self.opts['pxMode']:
|
if self.opts['pxMode']:
|
||||||
mask = np.equal(dataSet['fragCoords'], None)
|
mask = np.equal(dataSet['fragCoords'], None)
|
||||||
if np.any(mask):
|
if np.any(mask):
|
||||||
@ -549,6 +558,13 @@ class ScatterPlotItem(GraphicsObject):
|
|||||||
#if rec['fragCoords'] is None:
|
#if rec['fragCoords'] is None:
|
||||||
#invalidate = True
|
#invalidate = True
|
||||||
#rec['fragCoords'] = self.fragmentAtlas.getSymbolCoords(*self.getSpotOpts(rec))
|
#rec['fragCoords'] = self.fragmentAtlas.getSymbolCoords(*self.getSpotOpts(rec))
|
||||||
|
self.fragmentAtlas.getAtlas()
|
||||||
|
self._maxSpotPxWidth=self.fragmentAtlas.max_width
|
||||||
|
else:
|
||||||
|
self._maxSpotWidth = 0
|
||||||
|
self._maxSpotPxWidth = 0
|
||||||
|
self.measureSpotSizes(dataSet)
|
||||||
|
|
||||||
if invalidate:
|
if invalidate:
|
||||||
self.invalidate()
|
self.invalidate()
|
||||||
|
|
||||||
@ -711,7 +727,7 @@ class ScatterPlotItem(GraphicsObject):
|
|||||||
#self.lastAtlas = arr
|
#self.lastAtlas = arr
|
||||||
|
|
||||||
if self.fragments is None:
|
if self.fragments is None:
|
||||||
self.updateSpots()
|
#self.updateSpots()
|
||||||
self.generateFragments()
|
self.generateFragments()
|
||||||
|
|
||||||
p.resetTransform()
|
p.resetTransform()
|
||||||
|
Loading…
Reference in New Issue
Block a user