Fixes for PlotCurveItem, PlotDataItem, ScatterPlotItem.
Made APIs more complete and consistent.
This commit is contained in:
parent
66dd6f974e
commit
59ed9397a3
|
@ -56,7 +56,7 @@ def update():
|
||||||
global curve, data, ptr, p6
|
global curve, data, ptr, p6
|
||||||
curve.setData(data[ptr%10])
|
curve.setData(data[ptr%10])
|
||||||
if ptr == 0:
|
if ptr == 0:
|
||||||
p6.enableAutoRange('xy', False)
|
p6.enableAutoRange('xy', False) ## stop auto-scaling after the first data set is plotted
|
||||||
ptr += 1
|
ptr += 1
|
||||||
timer = QtCore.QTimer()
|
timer = QtCore.QTimer()
|
||||||
timer.timeout.connect(update)
|
timer.timeout.connect(update)
|
||||||
|
|
|
@ -22,41 +22,34 @@ class PlotCurveItem(GraphicsObject):
|
||||||
sigPlotChanged = QtCore.Signal(object)
|
sigPlotChanged = QtCore.Signal(object)
|
||||||
sigClicked = QtCore.Signal(object)
|
sigClicked = QtCore.Signal(object)
|
||||||
|
|
||||||
def __init__(self, y=None, x=None, fillLevel=None, copy=False, pen=None, shadowPen=None, brush=None, parent=None, color=None, clickable=False):
|
def __init__(self, y=None, x=None, fillLevel=None, copy=False, pen=None, shadowPen=None, brush=None, parent=None, clickable=False):
|
||||||
GraphicsObject.__init__(self, parent)
|
GraphicsObject.__init__(self, parent)
|
||||||
self.clear()
|
self.clear()
|
||||||
self.path = None
|
self.path = None
|
||||||
self.fillPath = None
|
self.fillPath = None
|
||||||
if pen is None:
|
|
||||||
if color is None:
|
|
||||||
self.setPen((200,200,200))
|
|
||||||
else:
|
|
||||||
self.setPen(color)
|
|
||||||
else:
|
|
||||||
self.setPen(pen)
|
|
||||||
|
|
||||||
self.setShadowPen(shadowPen)
|
|
||||||
|
|
||||||
if y is not None:
|
if y is not None:
|
||||||
self.updateData(y, x, copy)
|
self.updateData(y, x)
|
||||||
|
|
||||||
## this is disastrous for performance.
|
## this is disastrous for performance.
|
||||||
#self.setCacheMode(QtGui.QGraphicsItem.DeviceCoordinateCache)
|
#self.setCacheMode(QtGui.QGraphicsItem.DeviceCoordinateCache)
|
||||||
|
|
||||||
self.fillLevel = fillLevel
|
|
||||||
self.brush = brush
|
|
||||||
|
|
||||||
self.metaData = {}
|
self.metaData = {}
|
||||||
self.opts = {
|
self.opts = {
|
||||||
'spectrumMode': False,
|
#'spectrumMode': False,
|
||||||
'logMode': [False, False],
|
#'logMode': [False, False],
|
||||||
'pointMode': False,
|
#'downsample': False,
|
||||||
'pointStyle': None,
|
#'alphaHint': 1.0,
|
||||||
'downsample': False,
|
#'alphaMode': False,
|
||||||
'alphaHint': 1.0,
|
'pen': 'w',
|
||||||
'alphaMode': False
|
'shadowPen': None,
|
||||||
|
'fillLevel': fillLevel,
|
||||||
|
'brush': brush,
|
||||||
}
|
}
|
||||||
|
self.setPen(pen)
|
||||||
|
self.setShadowPen(shadowPen)
|
||||||
|
self.setFillLevel(fillLevel)
|
||||||
|
self.setBrush(brush)
|
||||||
self.setClickable(clickable)
|
self.setClickable(clickable)
|
||||||
#self.fps = None
|
#self.fps = None
|
||||||
|
|
||||||
|
@ -71,35 +64,36 @@ class PlotCurveItem(GraphicsObject):
|
||||||
|
|
||||||
|
|
||||||
def getData(self):
|
def getData(self):
|
||||||
if self.xData is None:
|
return self.xData, self.yData
|
||||||
return (None, None)
|
#if self.xData is None:
|
||||||
if self.xDisp is None:
|
#return (None, None)
|
||||||
nanMask = np.isnan(self.xData) | np.isnan(self.yData)
|
#if self.xDisp is None:
|
||||||
if any(nanMask):
|
#nanMask = np.isnan(self.xData) | np.isnan(self.yData)
|
||||||
x = self.xData[~nanMask]
|
#if any(nanMask):
|
||||||
y = self.yData[~nanMask]
|
#x = self.xData[~nanMask]
|
||||||
else:
|
#y = self.yData[~nanMask]
|
||||||
x = self.xData
|
#else:
|
||||||
y = self.yData
|
#x = self.xData
|
||||||
ds = self.opts['downsample']
|
#y = self.yData
|
||||||
if ds > 1:
|
#ds = self.opts['downsample']
|
||||||
x = x[::ds]
|
#if ds > 1:
|
||||||
#y = resample(y[:len(x)*ds], len(x)) ## scipy.signal.resample causes nasty ringing
|
#x = x[::ds]
|
||||||
y = y[::ds]
|
##y = resample(y[:len(x)*ds], len(x)) ## scipy.signal.resample causes nasty ringing
|
||||||
if self.opts['spectrumMode']:
|
#y = y[::ds]
|
||||||
f = fft(y) / len(y)
|
#if self.opts['spectrumMode']:
|
||||||
y = abs(f[1:len(f)/2])
|
#f = fft(y) / len(y)
|
||||||
dt = x[-1] - x[0]
|
#y = abs(f[1:len(f)/2])
|
||||||
x = np.linspace(0, 0.5*len(x)/dt, len(y))
|
#dt = x[-1] - x[0]
|
||||||
if self.opts['logMode'][0]:
|
#x = np.linspace(0, 0.5*len(x)/dt, len(y))
|
||||||
x = np.log10(x)
|
#if self.opts['logMode'][0]:
|
||||||
if self.opts['logMode'][1]:
|
#x = np.log10(x)
|
||||||
y = np.log10(y)
|
#if self.opts['logMode'][1]:
|
||||||
self.xDisp = x
|
#y = np.log10(y)
|
||||||
self.yDisp = y
|
#self.xDisp = x
|
||||||
#print self.yDisp.shape, self.yDisp.min(), self.yDisp.max()
|
#self.yDisp = y
|
||||||
#print self.xDisp.shape, self.xDisp.min(), self.xDisp.max()
|
##print self.yDisp.shape, self.yDisp.min(), self.yDisp.max()
|
||||||
return self.xDisp, self.yDisp
|
##print self.xDisp.shape, self.xDisp.min(), self.xDisp.max()
|
||||||
|
#return self.xDisp, self.yDisp
|
||||||
|
|
||||||
#def generateSpecData(self):
|
#def generateSpecData(self):
|
||||||
#f = fft(self.yData) / len(self.yData)
|
#f = fft(self.yData) / len(self.yData)
|
||||||
|
@ -124,120 +118,121 @@ class PlotCurveItem(GraphicsObject):
|
||||||
else:
|
else:
|
||||||
return (scipy.stats.scoreatpercentile(d, 50 - (frac * 50)), scipy.stats.scoreatpercentile(d, 50 + (frac * 50)))
|
return (scipy.stats.scoreatpercentile(d, 50 - (frac * 50)), scipy.stats.scoreatpercentile(d, 50 + (frac * 50)))
|
||||||
|
|
||||||
def setMeta(self, data):
|
#def setMeta(self, data):
|
||||||
self.metaData = data
|
#self.metaData = data
|
||||||
|
|
||||||
def meta(self):
|
#def meta(self):
|
||||||
return self.metaData
|
#return self.metaData
|
||||||
|
|
||||||
def setPen(self, pen):
|
def setPen(self, *args, **kargs):
|
||||||
self.pen = fn.mkPen(pen)
|
self.opts['pen'] = fn.mkPen(*args, **kargs)
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def setColor(self, color):
|
def setShadowPen(self, *args, **kargs):
|
||||||
self.pen.setColor(color)
|
self.opts['shadowPen'] = fn.mkPen(*args, **kargs)
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def setAlpha(self, alpha, auto):
|
def setBrush(self, *args, **kargs):
|
||||||
self.opts['alphaHint'] = alpha
|
self.opts['brush'] = fn.mkBrush(*args, **kargs)
|
||||||
self.opts['alphaMode'] = auto
|
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def setSpectrumMode(self, mode):
|
def setFillLevel(self, level):
|
||||||
self.opts['spectrumMode'] = mode
|
self.opts['fillLevel'] = level
|
||||||
self.xDisp = self.yDisp = None
|
self.fillPath = None
|
||||||
self.path = None
|
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def setLogMode(self, mode):
|
#def setColor(self, color):
|
||||||
self.opts['logMode'] = mode
|
#self.pen.setColor(color)
|
||||||
self.xDisp = self.yDisp = None
|
#self.update()
|
||||||
self.path = None
|
|
||||||
self.update()
|
|
||||||
|
|
||||||
def setPointMode(self, mode):
|
#def setAlpha(self, alpha, auto):
|
||||||
self.opts['pointMode'] = mode
|
#self.opts['alphaHint'] = alpha
|
||||||
self.update()
|
#self.opts['alphaMode'] = auto
|
||||||
|
#self.update()
|
||||||
|
|
||||||
def setShadowPen(self, pen):
|
#def setSpectrumMode(self, mode):
|
||||||
self.shadowPen = fn.mkPen(pen)
|
#self.opts['spectrumMode'] = mode
|
||||||
self.update()
|
#self.xDisp = self.yDisp = None
|
||||||
|
#self.path = None
|
||||||
|
#self.update()
|
||||||
|
|
||||||
def setDownsampling(self, ds):
|
#def setLogMode(self, mode):
|
||||||
if self.opts['downsample'] != ds:
|
#self.opts['logMode'] = mode
|
||||||
self.opts['downsample'] = ds
|
#self.xDisp = self.yDisp = None
|
||||||
self.xDisp = self.yDisp = None
|
#self.path = None
|
||||||
self.path = None
|
#self.update()
|
||||||
self.update()
|
|
||||||
|
|
||||||
def setData(self, x, y, copy=False):
|
#def setPointMode(self, mode):
|
||||||
"""For Qwt compatibility"""
|
#self.opts['pointMode'] = mode
|
||||||
self.updateData(y, x, copy)
|
#self.update()
|
||||||
|
|
||||||
def updateData(self, data, x=None, copy=False):
|
|
||||||
|
#def setDownsampling(self, ds):
|
||||||
|
#if self.opts['downsample'] != ds:
|
||||||
|
#self.opts['downsample'] = ds
|
||||||
|
#self.xDisp = self.yDisp = None
|
||||||
|
#self.path = None
|
||||||
|
#self.update()
|
||||||
|
|
||||||
|
def setData(self, *args, **kargs):
|
||||||
|
"""Same as updateData()"""
|
||||||
|
self.updateData(*args, **kargs)
|
||||||
|
|
||||||
|
def updateData(self, *args, **kargs):
|
||||||
prof = debug.Profiler('PlotCurveItem.updateData', disabled=True)
|
prof = debug.Profiler('PlotCurveItem.updateData', disabled=True)
|
||||||
|
|
||||||
|
if len(args) == 1:
|
||||||
|
kargs['y'] = args[0]
|
||||||
|
elif len(args) == 2:
|
||||||
|
kargs['x'] = args[0]
|
||||||
|
kargs['y'] = args[1]
|
||||||
|
|
||||||
|
if 'y' not in kargs or kargs['y'] is None:
|
||||||
|
kargs['y'] = np.array([])
|
||||||
|
if 'x' not in kargs or kargs['x'] is None:
|
||||||
|
kargs['x'] = np.arange(len(kargs['y']))
|
||||||
|
|
||||||
|
for k in ['x', 'y']:
|
||||||
|
data = kargs[k]
|
||||||
if isinstance(data, list):
|
if isinstance(data, list):
|
||||||
data = np.array(data)
|
kargs['k'] = np.array(data)
|
||||||
if isinstance(x, list):
|
if not isinstance(data, np.ndarray) or data.ndim > 1:
|
||||||
x = np.array(x)
|
raise Exception("Plot data must be 1D ndarray.")
|
||||||
if not isinstance(data, np.ndarray) or data.ndim > 2:
|
|
||||||
raise Exception("Plot data must be 1 or 2D ndarray (data shape is %s)" % str(data.shape))
|
|
||||||
if x == None:
|
|
||||||
if 'complex' in str(data.dtype):
|
if 'complex' in str(data.dtype):
|
||||||
raise Exception("Can not plot complex data types.")
|
raise Exception("Can not plot complex data types.")
|
||||||
else:
|
|
||||||
if 'complex' in str(data.dtype)+str(x.dtype):
|
|
||||||
raise Exception("Can not plot complex data types.")
|
|
||||||
|
|
||||||
if data.ndim == 2: ### If data is 2D array, then assume x and y values are in first two columns or rows.
|
|
||||||
if x is not None:
|
|
||||||
raise Exception("Plot data may be 2D only if no x argument is supplied.")
|
|
||||||
ax = 0
|
|
||||||
if data.shape[0] > 2 and data.shape[1] == 2:
|
|
||||||
ax = 1
|
|
||||||
ind = [slice(None), slice(None)]
|
|
||||||
ind[ax] = 0
|
|
||||||
y = data[tuple(ind)]
|
|
||||||
ind[ax] = 1
|
|
||||||
x = data[tuple(ind)]
|
|
||||||
elif data.ndim == 1:
|
|
||||||
y = data
|
|
||||||
prof.mark("data checks")
|
prof.mark("data checks")
|
||||||
|
|
||||||
self.setCacheMode(QtGui.QGraphicsItem.NoCache) ## Disabling and re-enabling the cache works around a bug in Qt 4.6 causing the cached results to display incorrectly
|
#self.setCacheMode(QtGui.QGraphicsItem.NoCache) ## Disabling and re-enabling the cache works around a bug in Qt 4.6 causing the cached results to display incorrectly
|
||||||
## Test this bug with test_PlotWidget and zoom in on the animated plot
|
## Test this bug with test_PlotWidget and zoom in on the animated plot
|
||||||
|
|
||||||
self.prepareGeometryChange()
|
self.prepareGeometryChange()
|
||||||
if copy:
|
self.yData = kargs['y'].view(np.ndarray)
|
||||||
self.yData = y.view(np.ndarray).copy()
|
self.xData = kargs['x'].view(np.ndarray)
|
||||||
else:
|
|
||||||
self.yData = y.view(np.ndarray)
|
|
||||||
|
|
||||||
if x is None:
|
|
||||||
self.xData = np.arange(0, self.yData.shape[0])
|
|
||||||
else:
|
|
||||||
if copy:
|
|
||||||
self.xData = x.view(np.ndarray).copy()
|
|
||||||
else:
|
|
||||||
self.xData = x.view(np.ndarray)
|
|
||||||
prof.mark('copy')
|
prof.mark('copy')
|
||||||
|
|
||||||
|
|
||||||
if self.xData.shape != self.yData.shape:
|
if self.xData.shape != self.yData.shape:
|
||||||
raise Exception("X and Y arrays must be the same shape--got %s and %s." % (str(x.shape), str(y.shape)))
|
raise Exception("X and Y arrays must be the same shape--got %s and %s." % (str(x.shape), str(y.shape)))
|
||||||
|
|
||||||
self.path = None
|
self.path = None
|
||||||
self.xDisp = self.yDisp = None
|
self.fillPath = None
|
||||||
|
#self.xDisp = self.yDisp = None
|
||||||
|
|
||||||
|
if 'pen' in kargs:
|
||||||
|
self.setPen(kargs['pen'])
|
||||||
|
if 'shadowPen' in kargs:
|
||||||
|
self.setShadowPen(kargs['shadowPen'])
|
||||||
|
if 'fillLevel' in kargs:
|
||||||
|
self.setFillLevel(kargs['fillLevel'])
|
||||||
|
if 'brush' in kargs:
|
||||||
|
self.setBrush(kargs['brush'])
|
||||||
|
|
||||||
|
|
||||||
prof.mark('set')
|
prof.mark('set')
|
||||||
self.update()
|
self.update()
|
||||||
prof.mark('update')
|
prof.mark('update')
|
||||||
#self.emit(QtCore.SIGNAL('plotChanged'), self)
|
|
||||||
self.sigPlotChanged.emit(self)
|
self.sigPlotChanged.emit(self)
|
||||||
prof.mark('emit')
|
prof.mark('emit')
|
||||||
#prof.finish()
|
|
||||||
#self.setCacheMode(QtGui.QGraphicsItem.DeviceCoordinateCache)
|
|
||||||
prof.mark('set cache mode')
|
|
||||||
prof.finish()
|
prof.finish()
|
||||||
|
|
||||||
def generatePath(self, x, y):
|
def generatePath(self, x, y):
|
||||||
|
@ -303,10 +298,10 @@ class PlotCurveItem(GraphicsObject):
|
||||||
return QtCore.QRectF()
|
return QtCore.QRectF()
|
||||||
|
|
||||||
|
|
||||||
if self.shadowPen is not None:
|
if self.opts['shadowPen'] is not None:
|
||||||
lineWidth = (max(self.pen.width(), self.shadowPen.width()) + 1)
|
lineWidth = (max(self.opts['pen'].width(), self.opts['shadowPen'].width()) + 1)
|
||||||
else:
|
else:
|
||||||
lineWidth = (self.pen.width()+1)
|
lineWidth = (self.opts['pen'].width()+1)
|
||||||
|
|
||||||
|
|
||||||
pixels = self.pixelVectors()
|
pixels = self.pixelVectors()
|
||||||
|
@ -343,34 +338,32 @@ class PlotCurveItem(GraphicsObject):
|
||||||
path = self.path
|
path = self.path
|
||||||
prof.mark('generate path')
|
prof.mark('generate path')
|
||||||
|
|
||||||
if self.brush is not None:
|
if self.opts['brush'] is not None and self.opts['fillLevel'] is not None:
|
||||||
if self.fillPath is None:
|
if self.fillPath is None:
|
||||||
if x is None:
|
if x is None:
|
||||||
x,y = self.getData()
|
x,y = self.getData()
|
||||||
p2 = QtGui.QPainterPath(self.path)
|
p2 = QtGui.QPainterPath(self.path)
|
||||||
p2.lineTo(x[-1], self.fillLevel)
|
p2.lineTo(x[-1], self.opts['fillLevel'])
|
||||||
p2.lineTo(x[0], self.fillLevel)
|
p2.lineTo(x[0], self.opts['fillLevel'])
|
||||||
|
p2.lineTo(x[0], y[0])
|
||||||
p2.closeSubpath()
|
p2.closeSubpath()
|
||||||
self.fillPath = p2
|
self.fillPath = p2
|
||||||
|
|
||||||
p.fillPath(self.fillPath, fn.mkBrush(self.brush))
|
p.fillPath(self.fillPath, self.opts['brush'])
|
||||||
|
|
||||||
if self.shadowPen is not None:
|
|
||||||
sp = QtGui.QPen(self.shadowPen)
|
|
||||||
else:
|
|
||||||
sp = None
|
|
||||||
|
|
||||||
## Copy pens and apply alpha adjustment
|
## Copy pens and apply alpha adjustment
|
||||||
cp = QtGui.QPen(self.pen)
|
sp = QtGui.QPen(self.opts['shadowPen'])
|
||||||
for pen in [sp, cp]:
|
cp = QtGui.QPen(self.opts['pen'])
|
||||||
if pen is None:
|
#for pen in [sp, cp]:
|
||||||
continue
|
#if pen is None:
|
||||||
c = pen.color()
|
#continue
|
||||||
c.setAlpha(c.alpha() * self.opts['alphaHint'])
|
#c = pen.color()
|
||||||
pen.setColor(c)
|
#c.setAlpha(c.alpha() * self.opts['alphaHint'])
|
||||||
#pen.setCosmetic(True)
|
#pen.setColor(c)
|
||||||
|
##pen.setCosmetic(True)
|
||||||
|
|
||||||
if self.shadowPen is not None:
|
if sp is not None:
|
||||||
p.setPen(sp)
|
p.setPen(sp)
|
||||||
p.drawPath(path)
|
p.drawPath(path)
|
||||||
p.setPen(cp)
|
p.setPen(cp)
|
||||||
|
|
|
@ -78,9 +78,16 @@ class PlotDataItem(GraphicsObject):
|
||||||
self.setFlag(self.ItemHasNoContents)
|
self.setFlag(self.ItemHasNoContents)
|
||||||
self.xData = None
|
self.xData = None
|
||||||
self.yData = None
|
self.yData = None
|
||||||
self.curves = []
|
self.xDisp = None
|
||||||
self.scatters = []
|
self.yDisp = None
|
||||||
self.clear()
|
#self.curves = []
|
||||||
|
#self.scatters = []
|
||||||
|
self.curve = PlotCurveItem()
|
||||||
|
self.scatter = ScatterPlotItem()
|
||||||
|
self.curve.setParentItem(self)
|
||||||
|
self.scatter.setParentItem(self)
|
||||||
|
|
||||||
|
#self.clear()
|
||||||
self.opts = {
|
self.opts = {
|
||||||
'fftMode': False,
|
'fftMode': False,
|
||||||
'logMode': [False, False],
|
'logMode': [False, False],
|
||||||
|
@ -130,17 +137,20 @@ class PlotDataItem(GraphicsObject):
|
||||||
self.opts['pointMode'] = mode
|
self.opts['pointMode'] = mode
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def setPen(self, pen):
|
def setPen(self, *args, **kargs):
|
||||||
"""
|
"""
|
||||||
| Sets the pen used to draw lines between points.
|
| Sets the pen used to draw lines between points.
|
||||||
| *pen* can be a QPen or any argument accepted by :func:`pyqtgraph.mkPen() <pyqtgraph.mkPen>`
|
| *pen* can be a QPen or any argument accepted by :func:`pyqtgraph.mkPen() <pyqtgraph.mkPen>`
|
||||||
"""
|
"""
|
||||||
self.opts['pen'] = fn.mkPen(pen)
|
pen = fn.mkPen(*args, **kargs)
|
||||||
for c in self.curves:
|
self.opts['pen'] = pen
|
||||||
c.setPen(pen)
|
#self.curve.setPen(pen)
|
||||||
self.update()
|
#for c in self.curves:
|
||||||
|
#c.setPen(pen)
|
||||||
|
#self.update()
|
||||||
|
self.updateItems()
|
||||||
|
|
||||||
def setShadowPen(self, pen):
|
def setShadowPen(self, *args, **kargs):
|
||||||
"""
|
"""
|
||||||
| Sets the shadow pen used to draw lines between points (this is for enhancing contrast or
|
| Sets the shadow pen used to draw lines between points (this is for enhancing contrast or
|
||||||
emphacizing data).
|
emphacizing data).
|
||||||
|
@ -148,10 +158,46 @@ class PlotDataItem(GraphicsObject):
|
||||||
and should generally be assigned greater width than the primary pen.
|
and should generally be assigned greater width than the primary pen.
|
||||||
| *pen* can be a QPen or any argument accepted by :func:`pyqtgraph.mkPen() <pyqtgraph.mkPen>`
|
| *pen* can be a QPen or any argument accepted by :func:`pyqtgraph.mkPen() <pyqtgraph.mkPen>`
|
||||||
"""
|
"""
|
||||||
|
pen = fn.mkPen(*args, **kargs)
|
||||||
self.opts['shadowPen'] = pen
|
self.opts['shadowPen'] = pen
|
||||||
for c in self.curves:
|
#for c in self.curves:
|
||||||
c.setPen(pen)
|
#c.setPen(pen)
|
||||||
self.update()
|
#self.update()
|
||||||
|
self.updateItems()
|
||||||
|
|
||||||
|
def setBrush(self, *args, **kargs):
|
||||||
|
brush = fn.mkBrush(*args, **kargs)
|
||||||
|
self.opts['brush'] = brush
|
||||||
|
self.updateItems()
|
||||||
|
|
||||||
|
def setFillLevel(self, level):
|
||||||
|
self.opts['fillLevel'] = level
|
||||||
|
self.updateItems()
|
||||||
|
|
||||||
|
def setSymbol(self, symbol):
|
||||||
|
self.opts['symbol'] = symbol
|
||||||
|
#self.scatter.setSymbol(symbol)
|
||||||
|
self.updateItems()
|
||||||
|
|
||||||
|
def setSymbolPen(self, *args, **kargs):
|
||||||
|
pen = fn.mkPen(*args, **kargs)
|
||||||
|
self.opts['symbolPen'] = pen
|
||||||
|
#self.scatter.setSymbolPen(pen)
|
||||||
|
self.updateItems()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def setSymbolBrush(self, *args, **kargs):
|
||||||
|
brush = fn.mkBrush(*args, **kargs)
|
||||||
|
self.opts['symbolBrush'] = brush
|
||||||
|
#self.scatter.setSymbolBrush(brush)
|
||||||
|
self.updateItems()
|
||||||
|
|
||||||
|
|
||||||
|
def setSymbolSize(self, size):
|
||||||
|
self.opts['symbolSize'] = size
|
||||||
|
#self.scatter.setSymbolSize(symbolSize)
|
||||||
|
self.updateItems()
|
||||||
|
|
||||||
def setDownsampling(self, ds):
|
def setDownsampling(self, ds):
|
||||||
if self.opts['downsample'] != ds:
|
if self.opts['downsample'] != ds:
|
||||||
|
@ -165,7 +211,7 @@ class PlotDataItem(GraphicsObject):
|
||||||
See :func:`__init__() <pyqtgraph.PlotDataItem.__init__>` for details; it accepts the same arguments.
|
See :func:`__init__() <pyqtgraph.PlotDataItem.__init__>` for details; it accepts the same arguments.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.clear()
|
#self.clear()
|
||||||
|
|
||||||
y = None
|
y = None
|
||||||
x = None
|
x = None
|
||||||
|
@ -219,7 +265,7 @@ class PlotDataItem(GraphicsObject):
|
||||||
|
|
||||||
|
|
||||||
## if symbol pen/brush are given with no symbol, then assume symbol is 'o'
|
## if symbol pen/brush are given with no symbol, then assume symbol is 'o'
|
||||||
if 'symbol' not in kargs and ('symbolPen' in kargs or 'symbolBrush' in kargs):
|
if 'symbol' not in kargs and ('symbolPen' in kargs or 'symbolBrush' in kargs or 'symbolSize' in kargs):
|
||||||
kargs['symbol'] = 'o'
|
kargs['symbol'] = 'o'
|
||||||
|
|
||||||
for k in self.opts.keys():
|
for k in self.opts.keys():
|
||||||
|
@ -251,6 +297,8 @@ class PlotDataItem(GraphicsObject):
|
||||||
|
|
||||||
self.xData = x.view(np.ndarray) ## one last check to make sure there are no MetaArrays getting by
|
self.xData = x.view(np.ndarray) ## one last check to make sure there are no MetaArrays getting by
|
||||||
self.yData = y.view(np.ndarray)
|
self.yData = y.view(np.ndarray)
|
||||||
|
self.xDisp = None
|
||||||
|
self.yDisp = None
|
||||||
|
|
||||||
self.updateItems()
|
self.updateItems()
|
||||||
view = self.getViewBox()
|
view = self.getViewBox()
|
||||||
|
@ -260,29 +308,37 @@ class PlotDataItem(GraphicsObject):
|
||||||
|
|
||||||
|
|
||||||
def updateItems(self):
|
def updateItems(self):
|
||||||
for c in self.curves+self.scatters:
|
#for c in self.curves+self.scatters:
|
||||||
if c.scene() is not None:
|
#if c.scene() is not None:
|
||||||
c.scene().removeItem(c)
|
#c.scene().removeItem(c)
|
||||||
|
|
||||||
curveArgs = {}
|
curveArgs = {}
|
||||||
for k in ['pen', 'shadowPen', 'fillLevel', 'brush']:
|
for k in ['pen', 'shadowPen', 'fillLevel', 'brush']:
|
||||||
curveArgs[k] = self.opts[k]
|
curveArgs[k] = self.opts[k]
|
||||||
|
|
||||||
scatterArgs = {}
|
scatterArgs = {}
|
||||||
for k,v in [('symbolPen','pen'), ('symbolBrush','brush'), ('symbol','symbol')]:
|
for k,v in [('symbolPen','pen'), ('symbolBrush','brush'), ('symbol','symbol'), ('symbolSize', 'size')]:
|
||||||
scatterArgs[v] = self.opts[k]
|
scatterArgs[v] = self.opts[k]
|
||||||
|
|
||||||
x,y = self.getData()
|
x,y = self.getData()
|
||||||
|
|
||||||
|
self.curve.setData(x=x, y=y, **curveArgs)
|
||||||
if curveArgs['pen'] is not None or curveArgs['brush'] is not None:
|
if curveArgs['pen'] is not None or curveArgs['brush'] is not None:
|
||||||
curve = PlotCurveItem(x=x, y=y, **curveArgs)
|
self.curve.show()
|
||||||
curve.setParentItem(self)
|
else:
|
||||||
self.curves.append(curve)
|
self.curve.hide()
|
||||||
|
#curve = PlotCurveItem(x=x, y=y, **curveArgs)
|
||||||
|
#curve.setParentItem(self)
|
||||||
|
#self.curves.append(curve)
|
||||||
|
|
||||||
|
self.scatter.setData(x=x, y=y, **scatterArgs)
|
||||||
if scatterArgs['symbol'] is not None:
|
if scatterArgs['symbol'] is not None:
|
||||||
sp = ScatterPlotItem(x=x, y=y, **scatterArgs)
|
self.scatter.show()
|
||||||
sp.setParentItem(self)
|
else:
|
||||||
self.scatters.append(sp)
|
self.scatter.hide()
|
||||||
|
#sp = ScatterPlotItem(x=x, y=y, **scatterArgs)
|
||||||
|
#sp.setParentItem(self)
|
||||||
|
#self.scatters.append(sp)
|
||||||
|
|
||||||
|
|
||||||
def getData(self):
|
def getData(self):
|
||||||
|
@ -335,15 +391,17 @@ class PlotDataItem(GraphicsObject):
|
||||||
|
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
for i in self.curves+self.scatters:
|
#for i in self.curves+self.scatters:
|
||||||
if i.scene() is not None:
|
#if i.scene() is not None:
|
||||||
i.scene().removeItem(i)
|
#i.scene().removeItem(i)
|
||||||
self.curves = []
|
#self.curves = []
|
||||||
self.scatters = []
|
#self.scatters = []
|
||||||
self.xData = None
|
self.xData = None
|
||||||
self.yData = None
|
self.yData = None
|
||||||
self.xDisp = None
|
self.xDisp = None
|
||||||
self.yDisp = None
|
self.yDisp = None
|
||||||
|
self.curve.setData([])
|
||||||
|
self.scatter.setData([])
|
||||||
|
|
||||||
def appendData(self, *args, **kargs):
|
def appendData(self, *args, **kargs):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -2,6 +2,8 @@ from pyqtgraph.Qt import QtGui, QtCore
|
||||||
from pyqtgraph.Point import Point
|
from pyqtgraph.Point import Point
|
||||||
import pyqtgraph.functions as fn
|
import pyqtgraph.functions as fn
|
||||||
from GraphicsObject import GraphicsObject
|
from GraphicsObject import GraphicsObject
|
||||||
|
import numpy as np
|
||||||
|
import scipy.stats
|
||||||
|
|
||||||
__all__ = ['ScatterPlotItem', 'SpotItem']
|
__all__ = ['ScatterPlotItem', 'SpotItem']
|
||||||
class ScatterPlotItem(GraphicsObject):
|
class ScatterPlotItem(GraphicsObject):
|
||||||
|
@ -10,64 +12,196 @@ class ScatterPlotItem(GraphicsObject):
|
||||||
sigClicked = QtCore.Signal(object, object) ## self, points
|
sigClicked = QtCore.Signal(object, object) ## self, points
|
||||||
sigPlotChanged = QtCore.Signal(object)
|
sigPlotChanged = QtCore.Signal(object)
|
||||||
|
|
||||||
def __init__(self, spots=None, x=None, y=None, pxMode=True, pen='default', brush='default', size=7,
|
def __init__(self, *args, **kargs):
|
||||||
symbol=None, identical=False, data=None):
|
"""
|
||||||
|
Accepts the same arguments as setData()
|
||||||
"""
|
"""
|
||||||
Arguments:
|
|
||||||
spots: list of dicts. Each dict specifies parameters for a single spot:
|
|
||||||
{'pos': (x,y), 'size', 'pen', 'brush', 'symbol'}
|
|
||||||
x,y: array of x,y values. Alternatively, specify spots['pos'] = (x,y)
|
|
||||||
pxMode: If True, spots are always the same size regardless of scaling, and size is given in px.
|
|
||||||
Otherwise, size is in scene coordinates and the spots scale with the view.
|
|
||||||
identical: If True, all spots are forced to look identical.
|
|
||||||
This can result in performance enhancement.
|
|
||||||
|
|
||||||
symbol can be one of:
|
GraphicsObject.__init__(self)
|
||||||
'o' circle
|
self.data = None
|
||||||
|
self.spots = []
|
||||||
|
self.bounds = [None, None]
|
||||||
|
self.opts = {}
|
||||||
|
self.spotsValid = False
|
||||||
|
self._spotPixmap = None
|
||||||
|
|
||||||
|
self.setPen(200,200,200)
|
||||||
|
self.setBrush(100,100,150)
|
||||||
|
self.setSymbol('o')
|
||||||
|
self.setSize(7)
|
||||||
|
self.setPxMode(True)
|
||||||
|
self.setIdentical(False)
|
||||||
|
|
||||||
|
self.setData(*args, **kargs)
|
||||||
|
|
||||||
|
|
||||||
|
def setData(self, *args, **kargs):
|
||||||
|
"""
|
||||||
|
Ordered Arguments:
|
||||||
|
If there is only one unnamed argument, it will be interpreted like the 'spots' argument.
|
||||||
|
|
||||||
|
If there are two unnamed arguments, they will be interpreted as sequences of x and y values.
|
||||||
|
|
||||||
|
Keyword Arguments:
|
||||||
|
*spots*: Optional list of dicts. Each dict specifies parameters for a single spot:
|
||||||
|
{'pos': (x,y), 'size', 'pen', 'brush', 'symbol'}. This is just an alternate method
|
||||||
|
of passing in data for the corresponding arguments.
|
||||||
|
*x*,*y*: 1D arrays of x,y values.
|
||||||
|
*pos*: 2D structure of x,y pairs (such as Nx2 array or list of tuples)
|
||||||
|
*pxMode*: If True, spots are always the same size regardless of scaling, and size is given in px.
|
||||||
|
Otherwise, size is in scene coordinates and the spots scale with the view.
|
||||||
|
Default is True
|
||||||
|
*identical*: If True, all spots are forced to look identical.
|
||||||
|
This can result in performance enhancement.
|
||||||
|
Default is False
|
||||||
|
*symbol* can be one (or a list) of:
|
||||||
|
'o' circle (default)
|
||||||
's' square
|
's' square
|
||||||
't' triangle
|
't' triangle
|
||||||
'd' diamond
|
'd' diamond
|
||||||
'+' plus
|
'+' plus
|
||||||
|
|
||||||
|
*pen*: The pen (or list of pens) to use for drawing spot outlines.
|
||||||
|
*brush*: The brush (or list of brushes) to use for filling spots.
|
||||||
|
*size*: The size (or list of sizes) of spots. If *pxMode* is True, this value is in pixels. Otherwise,
|
||||||
|
it is in the item's local coordinate system.
|
||||||
|
*data*: a list of python objects used to uniquely identify each spot.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
self.clear()
|
||||||
|
|
||||||
GraphicsObject.__init__(self)
|
|
||||||
self.spots = []
|
|
||||||
self.range = [[0,0], [0,0]]
|
|
||||||
self.identical = identical
|
|
||||||
self._spotPixmap = None
|
|
||||||
|
|
||||||
if brush == 'default':
|
## deal with non-keyword arguments
|
||||||
self.brush = QtGui.QBrush(QtGui.QColor(100, 100, 150))
|
if len(args) == 1:
|
||||||
|
kargs['spots'] = args[0]
|
||||||
|
elif len(args) == 2:
|
||||||
|
kargs['x'] = args[0]
|
||||||
|
kargs['y'] = args[1]
|
||||||
|
elif len(args) > 2:
|
||||||
|
raise Exception('Only accepts up to two non-keyword arguments.')
|
||||||
|
|
||||||
|
## convert 'pos' argument to 'x' and 'y'
|
||||||
|
if 'pos' in kargs:
|
||||||
|
pos = kargs['pos']
|
||||||
|
if isinstance(pos, np.ndarray):
|
||||||
|
kargs['x'] = pos[:,0]
|
||||||
|
kargs['y'] = pos[:,1]
|
||||||
else:
|
else:
|
||||||
self.brush = fn.mkBrush(brush)
|
x = []
|
||||||
|
y = []
|
||||||
if pen == 'default':
|
for p in pos:
|
||||||
self.pen = QtGui.QPen(QtGui.QColor(200, 200, 200))
|
if isinstance(p, QtCore.QPointF):
|
||||||
|
x.append(p.x())
|
||||||
|
y.append(p.y())
|
||||||
else:
|
else:
|
||||||
self.pen = fn.mkPen(pen)
|
x.append(p[0])
|
||||||
|
y.append(p[1])
|
||||||
|
kargs['x'] = x
|
||||||
|
kargs['y'] = y
|
||||||
|
|
||||||
self.symbol = symbol
|
## determine how many spots we have
|
||||||
self.size = size
|
if 'spots' in kargs:
|
||||||
|
numPts = len(kargs['spots'])
|
||||||
|
elif 'y' in kargs and kargs['y'] is not None:
|
||||||
|
numPts = len(kargs['y'])
|
||||||
|
else:
|
||||||
|
kargs['x'] = []
|
||||||
|
kargs['y'] = []
|
||||||
|
numPts = 0
|
||||||
|
|
||||||
self.pxMode = pxMode
|
## create empty record array
|
||||||
if spots is not None or x is not None:
|
self.data = np.empty(numPts, dtype=[('x', float), ('y', float), ('size', float), ('symbol', 'S1'), ('pen', object), ('brush', object), ('data', object), ('spot', object)])
|
||||||
self.setPoints(spots, x, y, data)
|
self.data['size'] = -1 ## indicates use default size
|
||||||
|
self.data['symbol'] = ''
|
||||||
|
self.data['pen'] = None
|
||||||
|
self.data['brush'] = None
|
||||||
|
self.data['data'] = None
|
||||||
|
|
||||||
#self.optimize = optimize
|
if 'spots' in kargs:
|
||||||
#if optimize:
|
spots = kargs['spots']
|
||||||
#self.spotImage = QtGui.QImage(size, size, QtGui.QImage.Format_ARGB32_Premultiplied)
|
for i in xrange(len(spots)):
|
||||||
#self.spotImage.fill(0)
|
spot = spots[i]
|
||||||
#p = QtGui.QPainter(self.spotImage)
|
for k in spot:
|
||||||
#p.setRenderHint(p.Antialiasing)
|
if k == 'pen':
|
||||||
#p.setBrush(brush)
|
self.data[i][k] = fn.mkPen(spot[k])
|
||||||
#p.setPen(pen)
|
elif k == 'brush':
|
||||||
#p.drawEllipse(0, 0, size, size)
|
self.data[i][k] = fn.mkBrush(spot[k])
|
||||||
#p.end()
|
elif k == 'pos':
|
||||||
#self.optimizePixmap = QtGui.QPixmap(self.spotImage)
|
pos = spot[k]
|
||||||
#self.optimizeFragments = []
|
if isinstance(pos, QtCore.QPointF):
|
||||||
#self.setFlags(self.flags() | self.ItemIgnoresTransformations)
|
x,y = pos.x(), pos.y()
|
||||||
|
else:
|
||||||
|
x,y = pos[0], pos[1]
|
||||||
|
self.data[i]['x'] = x
|
||||||
|
self.data[i]['y'] = y
|
||||||
|
elif k in ['x', 'y', 'size', 'symbol', 'data']:
|
||||||
|
self.data[i][k] = spot[k]
|
||||||
|
else:
|
||||||
|
raise Exception("Unknown spot parameter: %s" % k)
|
||||||
|
elif 'y' in kargs:
|
||||||
|
self.data['x'] = kargs['x']
|
||||||
|
self.data['y'] = kargs['y']
|
||||||
|
|
||||||
|
|
||||||
|
## Set any extra parameters provided in keyword arguments
|
||||||
|
for k in ['pxMode', 'identical', 'pen', 'brush', 'symbol', 'size']:
|
||||||
|
if k in kargs:
|
||||||
|
setMethod = getattr(self, 'set' + k[0].upper() + k[1:])
|
||||||
|
setMethod(kargs[k])
|
||||||
|
|
||||||
|
self.updateSpots()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#pen = kargs.get('pen', (200,200,200))
|
||||||
|
#brush = kargs.get('pen', (100,100,150))
|
||||||
|
|
||||||
|
#if hasattr(pen, '__len__'):
|
||||||
|
#pen = map(pg.mkPen(pen))
|
||||||
|
#self.data['pen'] = pen
|
||||||
|
|
||||||
|
#if hasattr(pen, '__len__'):
|
||||||
|
#brush = map(pg.mkPen(pen))
|
||||||
|
#self.data['brush'] = pen
|
||||||
|
|
||||||
|
#self.data['size'] = kargs.get('size', 7)
|
||||||
|
#self.data['symbol'] = kargs.get('symbol', 'o')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#if spots is not None and len(spots) > 0:
|
||||||
|
#spot = spots[0]
|
||||||
|
#for k in spot:
|
||||||
|
#self.data[k] = []
|
||||||
|
#for spot in spots:
|
||||||
|
#for k,v in spot.iteritems():
|
||||||
|
#self.data[k].append(v)
|
||||||
|
|
||||||
|
def setPoints(self, *args, **kargs):
|
||||||
|
"""Deprecated; use setData"""
|
||||||
|
return self.setData(*args, **kargs)
|
||||||
|
|
||||||
|
#def setPoints(self, spots=None, x=None, y=None, data=None):
|
||||||
|
#"""
|
||||||
|
#Remove all existing points in the scatter plot and add a new set.
|
||||||
|
#Arguments:
|
||||||
|
#spots - list of dicts specifying parameters for each spot
|
||||||
|
#[ {'pos': (x,y), 'pen': 'r', ...}, ...]
|
||||||
|
#x, y - arrays specifying location of spots to add.
|
||||||
|
#all other parameters (pen, symbol, etc.) will be set to the default
|
||||||
|
#values for this scatter plot.
|
||||||
|
#these arguments are IGNORED if 'spots' is specified
|
||||||
|
#data - list of arbitrary objects to be assigned to spot.data for each spot
|
||||||
|
#(this is useful for identifying spots that are clicked on)
|
||||||
|
#"""
|
||||||
|
#self.clear()
|
||||||
|
#self.bounds = [[0,0],[0,0]]
|
||||||
|
#self.addPoints(spots, x, y, data)
|
||||||
|
|
||||||
def implements(self, interface=None):
|
def implements(self, interface=None):
|
||||||
ints = ['plotData']
|
ints = ['plotData']
|
||||||
|
@ -75,8 +209,67 @@ class ScatterPlotItem(GraphicsObject):
|
||||||
return ints
|
return ints
|
||||||
return interface in ints
|
return interface in ints
|
||||||
|
|
||||||
|
def setPen(self, *args, **kargs):
|
||||||
|
if len(args) == 1 and (isinstance(args[0], np.ndarray) or isinstance(args[0], list)):
|
||||||
|
pens = args[0]
|
||||||
|
if self.data is None:
|
||||||
|
raise Exception("Must set data before setting multiple pens.")
|
||||||
|
if len(pens) != len(self.data):
|
||||||
|
raise Exception("Number of pens does not match number of points (%d != %d)" % (len(pens), len(self.data)))
|
||||||
|
for i in xrange(len(pens)):
|
||||||
|
self.data[i]['pen'] = fn.mkPen(pens[i])
|
||||||
|
else:
|
||||||
|
self.opts['pen'] = fn.mkPen(*args, **kargs)
|
||||||
|
self.updateSpots()
|
||||||
|
|
||||||
|
def setBrush(self, *args, **kargs):
|
||||||
|
if len(args) == 1 and (isinstance(args[0], np.ndarray) or isinstance(args[0], list)):
|
||||||
|
brushes = args[0]
|
||||||
|
if self.data is None:
|
||||||
|
raise Exception("Must set data before setting multiple brushes.")
|
||||||
|
if len(brushes) != len(self.data):
|
||||||
|
raise Exception("Number of brushes does not match number of points (%d != %d)" % (len(brushes), len(self.data)))
|
||||||
|
for i in xrange(len(brushes)):
|
||||||
|
self.data[i]['brush'] = fn.mkBrush(brushes[i], **kargs)
|
||||||
|
else:
|
||||||
|
self.opts['brush'] = fn.mkBrush(*args, **kargs)
|
||||||
|
self.updateSpots()
|
||||||
|
|
||||||
|
def setSymbol(self, symbol):
|
||||||
|
if isinstance(symbol, np.ndarray) or isinstance(symbol, list):
|
||||||
|
symbols = symbol
|
||||||
|
if self.data is None:
|
||||||
|
raise Exception("Must set data before setting multiple symbols.")
|
||||||
|
if len(symbols) != len(self.data):
|
||||||
|
raise Exception("Number of symbols does not match number of points (%d != %d)" % (len(symbols), len(self.data)))
|
||||||
|
self.data['symbol'] = symbols
|
||||||
|
else:
|
||||||
|
self.opts['symbol'] = symbol
|
||||||
|
self.updateSpots()
|
||||||
|
|
||||||
|
def setSize(self, size):
|
||||||
|
if isinstance(size, np.ndarray) or isinstance(size, list):
|
||||||
|
sizes = size
|
||||||
|
if self.data is None:
|
||||||
|
raise Exception("Must set data before setting multiple sizes.")
|
||||||
|
if len(sizes) != len(self.data):
|
||||||
|
raise Exception("Number of sizes does not match number of points (%d != %d)" % (len(sizes), len(self.data)))
|
||||||
|
self.data['size'] = sizes
|
||||||
|
else:
|
||||||
|
self.opts['size'] = size
|
||||||
|
self.updateSpots()
|
||||||
|
|
||||||
|
def setIdentical(self, ident):
|
||||||
|
self.opts['identical'] = ident
|
||||||
|
self.updateSpots()
|
||||||
|
|
||||||
def setPxMode(self, mode):
|
def setPxMode(self, mode):
|
||||||
self.pxMode = mode
|
self.opts['pxMode'] = mode
|
||||||
|
self.updateSpots()
|
||||||
|
|
||||||
|
def updateSpots(self):
|
||||||
|
self.spotsValid = False
|
||||||
|
self.update()
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
for i in self.spots:
|
for i in self.spots:
|
||||||
|
@ -85,73 +278,113 @@ class ScatterPlotItem(GraphicsObject):
|
||||||
if s is not None:
|
if s is not None:
|
||||||
s.removeItem(i)
|
s.removeItem(i)
|
||||||
self.spots = []
|
self.spots = []
|
||||||
|
self.data = None
|
||||||
|
self.spotsValid = False
|
||||||
|
self.bounds = [None, None]
|
||||||
|
|
||||||
|
|
||||||
def getRange(self, ax, percent):
|
def dataBounds(self, ax, frac=1.0):
|
||||||
return self.range[ax]
|
if frac >= 1.0 and self.bounds[ax] is not None:
|
||||||
|
return self.bounds[ax]
|
||||||
|
|
||||||
def setPoints(self, spots=None, x=None, y=None, data=None):
|
if self.data is None or len(self.data) == 0:
|
||||||
|
return (None, None)
|
||||||
|
|
||||||
|
if ax == 0:
|
||||||
|
d = self.data['x']
|
||||||
|
elif ax == 1:
|
||||||
|
d = self.data['y']
|
||||||
|
|
||||||
|
if frac >= 1.0:
|
||||||
|
minIndex = np.argmin(d)
|
||||||
|
maxIndex = np.argmax(d)
|
||||||
|
minVal = d[minIndex]
|
||||||
|
maxVal = d[maxIndex]
|
||||||
|
if not self.opts['pxMode']:
|
||||||
|
minVal -= self.data[minIndex]['size']
|
||||||
|
maxVal += self.data[maxIndex]['size']
|
||||||
|
self.bounds[ax] = (minVal, maxVal)
|
||||||
|
return self.bounds[ax]
|
||||||
|
elif frac <= 0.0:
|
||||||
|
raise Exception("Value for parameter 'frac' must be > 0. (got %s)" % str(frac))
|
||||||
|
else:
|
||||||
|
return (scipy.stats.scoreatpercentile(d, 50 - (frac * 50)), scipy.stats.scoreatpercentile(d, 50 + (frac * 50)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def addPoints(self, *args, **kargs):
|
||||||
"""
|
"""
|
||||||
Remove all existing points in the scatter plot and add a new set.
|
Add new points to the scatter plot.
|
||||||
Arguments:
|
Arguments are the same as setData()
|
||||||
spots - list of dicts specifying parameters for each spot
|
Note: this is expensive; plenty of room for optimization here.
|
||||||
[ {'pos': (x,y), 'pen': 'r', ...}, ...]
|
|
||||||
x, y - arrays specifying location of spots to add.
|
|
||||||
all other parameters (pen, symbol, etc.) will be set to the default
|
|
||||||
values for this scatter plot.
|
|
||||||
these arguments are IGNORED if 'spots' is specified
|
|
||||||
data - list of arbitrary objects to be assigned to spot.data for each spot
|
|
||||||
(this is useful for identifying spots that are clicked on)
|
|
||||||
"""
|
"""
|
||||||
self.clear()
|
if self.data is None:
|
||||||
self.range = [[0,0],[0,0]]
|
self.setData(*args, **kargs)
|
||||||
self.addPoints(spots, x, y, data)
|
return
|
||||||
|
|
||||||
def addPoints(self, spots=None, x=None, y=None, data=None):
|
|
||||||
|
data1 = self.data[:]
|
||||||
|
#range1 = [self.bounds[0][:], self.bounds[1][:]]
|
||||||
|
self.setData(*args, **kargs)
|
||||||
|
newData = np.empty(len(self.data) + len(data1), dtype=self.data.dtype)
|
||||||
|
newData[:len(data1)] = data1
|
||||||
|
newData[len(data1):] = self.data
|
||||||
|
#self.bounds = [
|
||||||
|
#[min(self.bounds[0][0], range1[0][0]), max(self.bounds[0][1], range1[0][1])],
|
||||||
|
#[min(self.bounds[1][0], range1[1][0]), max(self.bounds[1][1], range1[1][1])],
|
||||||
|
#]
|
||||||
|
self.data = newData
|
||||||
|
self.sigPlotChanged.emit(self)
|
||||||
|
|
||||||
|
|
||||||
|
def generateSpots(self):
|
||||||
xmn = ymn = xmx = ymx = None
|
xmn = ymn = xmx = ymx = None
|
||||||
if spots is not None:
|
|
||||||
n = len(spots)
|
|
||||||
else:
|
|
||||||
n = len(x)
|
|
||||||
|
|
||||||
for i in range(n):
|
## apply defaults
|
||||||
if spots is not None:
|
size = self.data['size'].copy()
|
||||||
s = spots[i]
|
size[size<0] = self.opts['size']
|
||||||
pos = Point(s['pos'])
|
|
||||||
else:
|
|
||||||
s = {}
|
|
||||||
pos = Point(x[i], y[i])
|
|
||||||
if data is not None:
|
|
||||||
s['data'] = data[i]
|
|
||||||
|
|
||||||
size = s.get('size', self.size)
|
pen = self.data['pen'].copy()
|
||||||
if self.pxMode:
|
pen[pen<0] = self.opts['pen'] ## note pen<0 checks for pen==None
|
||||||
|
|
||||||
|
brush = self.data['brush'].copy()
|
||||||
|
brush[brush<0] = self.opts['brush']
|
||||||
|
|
||||||
|
symbol = self.data['symbol'].copy()
|
||||||
|
symbol[symbol==''] = self.opts['symbol']
|
||||||
|
|
||||||
|
for i in xrange(len(self.data)):
|
||||||
|
s = self.data[i]
|
||||||
|
pos = Point(s['x'], s['y'])
|
||||||
|
if self.opts['pxMode']:
|
||||||
psize = 0
|
psize = 0
|
||||||
else:
|
else:
|
||||||
psize = size
|
psize = size[i]
|
||||||
if xmn is None:
|
|
||||||
xmn = pos[0]-psize
|
#if xmn is None:
|
||||||
xmx = pos[0]+psize
|
#xmn = pos[0]-psize
|
||||||
ymn = pos[1]-psize
|
#xmx = pos[0]+psize
|
||||||
ymx = pos[1]+psize
|
#ymn = pos[1]-psize
|
||||||
else:
|
#ymx = pos[1]+psize
|
||||||
xmn = min(xmn, pos[0]-psize)
|
#else:
|
||||||
xmx = max(xmx, pos[0]+psize)
|
#xmn = min(xmn, pos[0]-psize)
|
||||||
ymn = min(ymn, pos[1]-psize)
|
#xmx = max(xmx, pos[0]+psize)
|
||||||
ymx = max(ymx, pos[1]+psize)
|
#ymn = min(ymn, pos[1]-psize)
|
||||||
#print pos, xmn, xmx, ymn, ymx
|
#ymx = max(ymx, pos[1]+psize)
|
||||||
brush = s.get('brush', self.brush)
|
|
||||||
pen = s.get('pen', self.pen)
|
item = self.mkSpot(pos, size[i], self.opts['pxMode'], brush[i], pen[i], s['data'], symbol=symbol[i], index=len(self.spots))
|
||||||
pen.setCosmetic(True)
|
|
||||||
symbol = s.get('symbol', self.symbol)
|
|
||||||
data2 = s.get('data', None)
|
|
||||||
item = self.mkSpot(pos, size, self.pxMode, brush, pen, data2, symbol=symbol, index=len(self.spots))
|
|
||||||
self.spots.append(item)
|
self.spots.append(item)
|
||||||
|
self.data[i]['spot'] = item
|
||||||
#if self.optimize:
|
#if self.optimize:
|
||||||
#item.hide()
|
#item.hide()
|
||||||
#frag = QtGui.QPainter.PixmapFragment.create(pos, QtCore.QRectF(0, 0, size, size))
|
#frag = QtGui.QPainter.PixmapFragment.create(pos, QtCore.QRectF(0, 0, size, size))
|
||||||
#self.optimizeFragments.append(frag)
|
#self.optimizeFragments.append(frag)
|
||||||
self.range = [[xmn, xmx], [ymn, ymx]]
|
|
||||||
|
#self.bounds = [[xmn, xmx], [ymn, ymx]]
|
||||||
|
self.spotsValid = True
|
||||||
|
self.sigPlotChanged.emit(self)
|
||||||
|
|
||||||
|
|
||||||
#def setPointSize(self, size):
|
#def setPointSize(self, size):
|
||||||
#for s in self.spots:
|
#for s in self.spots:
|
||||||
|
@ -166,24 +399,22 @@ class ScatterPlotItem(GraphicsObject):
|
||||||
#p.drawPixmapFragments(self.optimizeFragments, self.optimizePixmap)
|
#p.drawPixmapFragments(self.optimizeFragments, self.optimizePixmap)
|
||||||
|
|
||||||
def paint(self, *args):
|
def paint(self, *args):
|
||||||
pass
|
if not self.spotsValid:
|
||||||
|
self.generateSpots()
|
||||||
|
|
||||||
def spotPixmap(self):
|
def spotPixmap(self):
|
||||||
## If all spots are identical, return the pixmap to use for all spots
|
## If all spots are identical, return the pixmap to use for all spots
|
||||||
## Otherwise return None
|
## Otherwise return None
|
||||||
|
|
||||||
if not self.identical:
|
if not self.opts['identical']:
|
||||||
return None
|
return None
|
||||||
if self._spotPixmap is None:
|
if self._spotPixmap is None:
|
||||||
#print 'spotPixmap'
|
spot = SpotItem(size=self.opts['size'], pxMode=True, brush=self.opts['brush'], pen=self.opts['pen'], symbol=self.opts['symbol'])
|
||||||
spot = SpotItem(size=self.size, pxMode=True, brush=self.brush, pen=self.pen, symbol=self.symbol)
|
|
||||||
#self._spotPixmap = PixmapSpotItem.makeSpotImage(self.size, self.pen, self.brush, self.symbol)
|
|
||||||
self._spotPixmap = spot.pixmap
|
self._spotPixmap = spot.pixmap
|
||||||
return self._spotPixmap
|
return self._spotPixmap
|
||||||
|
|
||||||
def mkSpot(self, pos, size, pxMode, brush, pen, data, symbol=None, index=None):
|
def mkSpot(self, pos, size, pxMode, brush, pen, data, symbol=None, index=None):
|
||||||
## Make and return a SpotItem (or PixmapSpotItem if in pxMode)
|
## Make and return a SpotItem (or PixmapSpotItem if in pxMode)
|
||||||
|
|
||||||
brush = fn.mkBrush(brush)
|
brush = fn.mkBrush(brush)
|
||||||
pen = fn.mkPen(pen)
|
pen = fn.mkPen(pen)
|
||||||
if pxMode:
|
if pxMode:
|
||||||
|
@ -198,10 +429,19 @@ class ScatterPlotItem(GraphicsObject):
|
||||||
return item
|
return item
|
||||||
|
|
||||||
def boundingRect(self):
|
def boundingRect(self):
|
||||||
((xmn, xmx), (ymn, ymx)) = self.range
|
(xmn, xmx) = self.dataBounds(ax=0)
|
||||||
if xmn is None or xmx is None or ymn is None or ymx is None:
|
(ymn, ymx) = self.dataBounds(ax=1)
|
||||||
return QtCore.QRectF()
|
if xmn is None or xmx is None:
|
||||||
|
xmn = 0
|
||||||
|
xmx = 0
|
||||||
|
if ymn is None or ymx is None:
|
||||||
|
ymn = 0
|
||||||
|
ymx = 0
|
||||||
return QtCore.QRectF(xmn, ymn, xmx-xmn, ymx-ymn)
|
return QtCore.QRectF(xmn, ymn, xmx-xmn, ymx-ymn)
|
||||||
|
|
||||||
|
#if xmn is None or xmx is None or ymn is None or ymx is None:
|
||||||
|
#return QtCore.QRectF()
|
||||||
|
#return QtCore.QRectF(xmn, ymn, xmx-xmn, ymx-ymn)
|
||||||
#return QtCore.QRectF(xmn-1, ymn-1, xmx-xmn+2, ymx-ymn+2)
|
#return QtCore.QRectF(xmn-1, ymn-1, xmx-xmn+2, ymx-ymn+2)
|
||||||
|
|
||||||
#def pointClicked(self, point):
|
#def pointClicked(self, point):
|
||||||
|
@ -222,7 +462,7 @@ class ScatterPlotItem(GraphicsObject):
|
||||||
sx = sp.x()
|
sx = sp.x()
|
||||||
sy = sp.y()
|
sy = sp.y()
|
||||||
s2x = s2y = ss * 0.5
|
s2x = s2y = ss * 0.5
|
||||||
if self.pxMode:
|
if self.opts['pxMode']:
|
||||||
s2x *= pw
|
s2x *= pw
|
||||||
s2y *= ph
|
s2y *= ph
|
||||||
if x > sx-s2x and x < sx+s2x and y > sy-s2y and y < sy+s2y:
|
if x > sx-s2x and x < sx+s2x and y > sy-s2y and y < sy+s2y:
|
||||||
|
@ -281,10 +521,18 @@ class SpotItem(GraphicsObject):
|
||||||
GraphicsObject.__init__(self)
|
GraphicsObject.__init__(self)
|
||||||
self.pxMode = pxMode
|
self.pxMode = pxMode
|
||||||
|
|
||||||
|
try:
|
||||||
|
symbol = int(symbol)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
if symbol is None:
|
if symbol is None:
|
||||||
symbol = 'o' ## circle by default
|
symbol = 'o' ## circle by default
|
||||||
elif isinstance(symbol, int): ## allow symbols specified by integer for easy iteration
|
elif isinstance(symbol, int): ## allow symbols specified by integer for easy iteration
|
||||||
symbol = ['o', 's', 't', 'd', '+'][symbol]
|
symbol = ['o', 's', 't', 'd', '+'][symbol]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
####print 'SpotItem symbol: ', symbol
|
####print 'SpotItem symbol: ', symbol
|
||||||
self.data = data
|
self.data = data
|
||||||
self.pen = pen
|
self.pen = pen
|
||||||
|
@ -294,11 +542,12 @@ class SpotItem(GraphicsObject):
|
||||||
self.symbol = symbol
|
self.symbol = symbol
|
||||||
#s2 = size/2.
|
#s2 = size/2.
|
||||||
self.path = QtGui.QPainterPath()
|
self.path = QtGui.QPainterPath()
|
||||||
|
|
||||||
if symbol == 'o':
|
if symbol == 'o':
|
||||||
self.path.addEllipse(QtCore.QRectF(-0.5, -0.5, 1, 1))
|
self.path.addEllipse(QtCore.QRectF(-0.5, -0.5, 1, 1))
|
||||||
elif symbol == 's':
|
elif symbol == 's':
|
||||||
self.path.addRect(QtCore.QRectF(-0.5, -0.5, 1, 1))
|
self.path.addRect(QtCore.QRectF(-0.5, -0.5, 1, 1))
|
||||||
elif symbol is 't' or symbol is '^':
|
elif symbol == 't' or symbol == '^':
|
||||||
self.path.moveTo(-0.5, -0.5)
|
self.path.moveTo(-0.5, -0.5)
|
||||||
self.path.lineTo(0, 0.5)
|
self.path.lineTo(0, 0.5)
|
||||||
self.path.lineTo(0.5, -0.5)
|
self.path.lineTo(0.5, -0.5)
|
||||||
|
@ -328,7 +577,7 @@ class SpotItem(GraphicsObject):
|
||||||
#self.path.connectPath(self.path)
|
#self.path.connectPath(self.path)
|
||||||
#elif symbol == 'x':
|
#elif symbol == 'x':
|
||||||
else:
|
else:
|
||||||
raise Exception("Unknown spot symbol '%s'" % symbol)
|
raise Exception("Unknown spot symbol '%s' (type=%s)" % (str(symbol), str(type(symbol))))
|
||||||
#self.path.addEllipse(QtCore.QRectF(-0.5, -0.5, 1, 1))
|
#self.path.addEllipse(QtCore.QRectF(-0.5, -0.5, 1, 1))
|
||||||
|
|
||||||
if pxMode:
|
if pxMode:
|
||||||
|
|
Loading…
Reference in New Issue
Block a user