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
|
||||
curve.setData(data[ptr%10])
|
||||
if ptr == 0:
|
||||
p6.enableAutoRange('xy', False)
|
||||
p6.enableAutoRange('xy', False) ## stop auto-scaling after the first data set is plotted
|
||||
ptr += 1
|
||||
timer = QtCore.QTimer()
|
||||
timer.timeout.connect(update)
|
||||
|
@ -22,41 +22,34 @@ class PlotCurveItem(GraphicsObject):
|
||||
sigPlotChanged = 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)
|
||||
self.clear()
|
||||
self.path = 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:
|
||||
self.updateData(y, x, copy)
|
||||
self.updateData(y, x)
|
||||
|
||||
## this is disastrous for performance.
|
||||
#self.setCacheMode(QtGui.QGraphicsItem.DeviceCoordinateCache)
|
||||
|
||||
self.fillLevel = fillLevel
|
||||
self.brush = brush
|
||||
|
||||
self.metaData = {}
|
||||
self.opts = {
|
||||
'spectrumMode': False,
|
||||
'logMode': [False, False],
|
||||
'pointMode': False,
|
||||
'pointStyle': None,
|
||||
'downsample': False,
|
||||
'alphaHint': 1.0,
|
||||
'alphaMode': False
|
||||
#'spectrumMode': False,
|
||||
#'logMode': [False, False],
|
||||
#'downsample': False,
|
||||
#'alphaHint': 1.0,
|
||||
#'alphaMode': False,
|
||||
'pen': 'w',
|
||||
'shadowPen': None,
|
||||
'fillLevel': fillLevel,
|
||||
'brush': brush,
|
||||
}
|
||||
|
||||
self.setPen(pen)
|
||||
self.setShadowPen(shadowPen)
|
||||
self.setFillLevel(fillLevel)
|
||||
self.setBrush(brush)
|
||||
self.setClickable(clickable)
|
||||
#self.fps = None
|
||||
|
||||
@ -71,35 +64,36 @@ class PlotCurveItem(GraphicsObject):
|
||||
|
||||
|
||||
def getData(self):
|
||||
if self.xData is None:
|
||||
return (None, None)
|
||||
if self.xDisp is None:
|
||||
nanMask = np.isnan(self.xData) | np.isnan(self.yData)
|
||||
if any(nanMask):
|
||||
x = self.xData[~nanMask]
|
||||
y = self.yData[~nanMask]
|
||||
else:
|
||||
x = self.xData
|
||||
y = self.yData
|
||||
ds = self.opts['downsample']
|
||||
if ds > 1:
|
||||
x = x[::ds]
|
||||
#y = resample(y[:len(x)*ds], len(x)) ## scipy.signal.resample causes nasty ringing
|
||||
y = y[::ds]
|
||||
if self.opts['spectrumMode']:
|
||||
f = fft(y) / len(y)
|
||||
y = abs(f[1:len(f)/2])
|
||||
dt = x[-1] - x[0]
|
||||
x = np.linspace(0, 0.5*len(x)/dt, len(y))
|
||||
if self.opts['logMode'][0]:
|
||||
x = np.log10(x)
|
||||
if self.opts['logMode'][1]:
|
||||
y = np.log10(y)
|
||||
self.xDisp = x
|
||||
self.yDisp = y
|
||||
#print self.yDisp.shape, self.yDisp.min(), self.yDisp.max()
|
||||
#print self.xDisp.shape, self.xDisp.min(), self.xDisp.max()
|
||||
return self.xDisp, self.yDisp
|
||||
return self.xData, self.yData
|
||||
#if self.xData is None:
|
||||
#return (None, None)
|
||||
#if self.xDisp is None:
|
||||
#nanMask = np.isnan(self.xData) | np.isnan(self.yData)
|
||||
#if any(nanMask):
|
||||
#x = self.xData[~nanMask]
|
||||
#y = self.yData[~nanMask]
|
||||
#else:
|
||||
#x = self.xData
|
||||
#y = self.yData
|
||||
#ds = self.opts['downsample']
|
||||
#if ds > 1:
|
||||
#x = x[::ds]
|
||||
##y = resample(y[:len(x)*ds], len(x)) ## scipy.signal.resample causes nasty ringing
|
||||
#y = y[::ds]
|
||||
#if self.opts['spectrumMode']:
|
||||
#f = fft(y) / len(y)
|
||||
#y = abs(f[1:len(f)/2])
|
||||
#dt = x[-1] - x[0]
|
||||
#x = np.linspace(0, 0.5*len(x)/dt, len(y))
|
||||
#if self.opts['logMode'][0]:
|
||||
#x = np.log10(x)
|
||||
#if self.opts['logMode'][1]:
|
||||
#y = np.log10(y)
|
||||
#self.xDisp = x
|
||||
#self.yDisp = y
|
||||
##print self.yDisp.shape, self.yDisp.min(), self.yDisp.max()
|
||||
##print self.xDisp.shape, self.xDisp.min(), self.xDisp.max()
|
||||
#return self.xDisp, self.yDisp
|
||||
|
||||
#def generateSpecData(self):
|
||||
#f = fft(self.yData) / len(self.yData)
|
||||
@ -124,120 +118,121 @@ class PlotCurveItem(GraphicsObject):
|
||||
else:
|
||||
return (scipy.stats.scoreatpercentile(d, 50 - (frac * 50)), scipy.stats.scoreatpercentile(d, 50 + (frac * 50)))
|
||||
|
||||
def setMeta(self, data):
|
||||
self.metaData = data
|
||||
#def setMeta(self, data):
|
||||
#self.metaData = data
|
||||
|
||||
def meta(self):
|
||||
return self.metaData
|
||||
#def meta(self):
|
||||
#return self.metaData
|
||||
|
||||
def setPen(self, pen):
|
||||
self.pen = fn.mkPen(pen)
|
||||
def setPen(self, *args, **kargs):
|
||||
self.opts['pen'] = fn.mkPen(*args, **kargs)
|
||||
self.update()
|
||||
|
||||
def setColor(self, color):
|
||||
self.pen.setColor(color)
|
||||
self.update()
|
||||
|
||||
def setAlpha(self, alpha, auto):
|
||||
self.opts['alphaHint'] = alpha
|
||||
self.opts['alphaMode'] = auto
|
||||
self.update()
|
||||
|
||||
def setSpectrumMode(self, mode):
|
||||
self.opts['spectrumMode'] = mode
|
||||
self.xDisp = self.yDisp = None
|
||||
self.path = None
|
||||
self.update()
|
||||
|
||||
def setLogMode(self, mode):
|
||||
self.opts['logMode'] = mode
|
||||
self.xDisp = self.yDisp = None
|
||||
self.path = None
|
||||
self.update()
|
||||
|
||||
def setPointMode(self, mode):
|
||||
self.opts['pointMode'] = mode
|
||||
self.update()
|
||||
|
||||
def setShadowPen(self, pen):
|
||||
self.shadowPen = fn.mkPen(pen)
|
||||
def setShadowPen(self, *args, **kargs):
|
||||
self.opts['shadowPen'] = fn.mkPen(*args, **kargs)
|
||||
self.update()
|
||||
|
||||
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, x, y, copy=False):
|
||||
"""For Qwt compatibility"""
|
||||
self.updateData(y, x, copy)
|
||||
def setBrush(self, *args, **kargs):
|
||||
self.opts['brush'] = fn.mkBrush(*args, **kargs)
|
||||
self.update()
|
||||
|
||||
def updateData(self, data, x=None, copy=False):
|
||||
def setFillLevel(self, level):
|
||||
self.opts['fillLevel'] = level
|
||||
self.fillPath = None
|
||||
self.update()
|
||||
|
||||
#def setColor(self, color):
|
||||
#self.pen.setColor(color)
|
||||
#self.update()
|
||||
|
||||
#def setAlpha(self, alpha, auto):
|
||||
#self.opts['alphaHint'] = alpha
|
||||
#self.opts['alphaMode'] = auto
|
||||
#self.update()
|
||||
|
||||
#def setSpectrumMode(self, mode):
|
||||
#self.opts['spectrumMode'] = mode
|
||||
#self.xDisp = self.yDisp = None
|
||||
#self.path = None
|
||||
#self.update()
|
||||
|
||||
#def setLogMode(self, mode):
|
||||
#self.opts['logMode'] = mode
|
||||
#self.xDisp = self.yDisp = None
|
||||
#self.path = None
|
||||
#self.update()
|
||||
|
||||
#def setPointMode(self, mode):
|
||||
#self.opts['pointMode'] = mode
|
||||
#self.update()
|
||||
|
||||
|
||||
#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)
|
||||
if isinstance(data, list):
|
||||
data = np.array(data)
|
||||
if isinstance(x, list):
|
||||
x = np.array(x)
|
||||
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 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):
|
||||
kargs['k'] = np.array(data)
|
||||
if not isinstance(data, np.ndarray) or data.ndim > 1:
|
||||
raise Exception("Plot data must be 1D ndarray.")
|
||||
if 'complex' in str(data.dtype):
|
||||
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")
|
||||
|
||||
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
|
||||
|
||||
self.prepareGeometryChange()
|
||||
if copy:
|
||||
self.yData = y.view(np.ndarray).copy()
|
||||
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)
|
||||
self.yData = kargs['y'].view(np.ndarray)
|
||||
self.xData = kargs['x'].view(np.ndarray)
|
||||
|
||||
prof.mark('copy')
|
||||
|
||||
|
||||
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)))
|
||||
|
||||
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')
|
||||
self.update()
|
||||
prof.mark('update')
|
||||
#self.emit(QtCore.SIGNAL('plotChanged'), self)
|
||||
self.sigPlotChanged.emit(self)
|
||||
prof.mark('emit')
|
||||
#prof.finish()
|
||||
#self.setCacheMode(QtGui.QGraphicsItem.DeviceCoordinateCache)
|
||||
prof.mark('set cache mode')
|
||||
prof.finish()
|
||||
|
||||
def generatePath(self, x, y):
|
||||
@ -303,10 +298,10 @@ class PlotCurveItem(GraphicsObject):
|
||||
return QtCore.QRectF()
|
||||
|
||||
|
||||
if self.shadowPen is not None:
|
||||
lineWidth = (max(self.pen.width(), self.shadowPen.width()) + 1)
|
||||
if self.opts['shadowPen'] is not None:
|
||||
lineWidth = (max(self.opts['pen'].width(), self.opts['shadowPen'].width()) + 1)
|
||||
else:
|
||||
lineWidth = (self.pen.width()+1)
|
||||
lineWidth = (self.opts['pen'].width()+1)
|
||||
|
||||
|
||||
pixels = self.pixelVectors()
|
||||
@ -343,34 +338,32 @@ class PlotCurveItem(GraphicsObject):
|
||||
path = self.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 x is None:
|
||||
x,y = self.getData()
|
||||
p2 = QtGui.QPainterPath(self.path)
|
||||
p2.lineTo(x[-1], self.fillLevel)
|
||||
p2.lineTo(x[0], self.fillLevel)
|
||||
p2.lineTo(x[-1], self.opts['fillLevel'])
|
||||
p2.lineTo(x[0], self.opts['fillLevel'])
|
||||
p2.lineTo(x[0], y[0])
|
||||
p2.closeSubpath()
|
||||
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
|
||||
cp = QtGui.QPen(self.pen)
|
||||
for pen in [sp, cp]:
|
||||
if pen is None:
|
||||
continue
|
||||
c = pen.color()
|
||||
c.setAlpha(c.alpha() * self.opts['alphaHint'])
|
||||
pen.setColor(c)
|
||||
#pen.setCosmetic(True)
|
||||
sp = QtGui.QPen(self.opts['shadowPen'])
|
||||
cp = QtGui.QPen(self.opts['pen'])
|
||||
#for pen in [sp, cp]:
|
||||
#if pen is None:
|
||||
#continue
|
||||
#c = pen.color()
|
||||
#c.setAlpha(c.alpha() * self.opts['alphaHint'])
|
||||
#pen.setColor(c)
|
||||
##pen.setCosmetic(True)
|
||||
|
||||
if self.shadowPen is not None:
|
||||
if sp is not None:
|
||||
p.setPen(sp)
|
||||
p.drawPath(path)
|
||||
p.setPen(cp)
|
||||
|
@ -78,9 +78,16 @@ class PlotDataItem(GraphicsObject):
|
||||
self.setFlag(self.ItemHasNoContents)
|
||||
self.xData = None
|
||||
self.yData = None
|
||||
self.curves = []
|
||||
self.scatters = []
|
||||
self.clear()
|
||||
self.xDisp = None
|
||||
self.yDisp = None
|
||||
#self.curves = []
|
||||
#self.scatters = []
|
||||
self.curve = PlotCurveItem()
|
||||
self.scatter = ScatterPlotItem()
|
||||
self.curve.setParentItem(self)
|
||||
self.scatter.setParentItem(self)
|
||||
|
||||
#self.clear()
|
||||
self.opts = {
|
||||
'fftMode': False,
|
||||
'logMode': [False, False],
|
||||
@ -130,17 +137,20 @@ class PlotDataItem(GraphicsObject):
|
||||
self.opts['pointMode'] = mode
|
||||
self.update()
|
||||
|
||||
def setPen(self, pen):
|
||||
def setPen(self, *args, **kargs):
|
||||
"""
|
||||
| Sets the pen used to draw lines between points.
|
||||
| *pen* can be a QPen or any argument accepted by :func:`pyqtgraph.mkPen() <pyqtgraph.mkPen>`
|
||||
"""
|
||||
self.opts['pen'] = fn.mkPen(pen)
|
||||
for c in self.curves:
|
||||
c.setPen(pen)
|
||||
self.update()
|
||||
pen = fn.mkPen(*args, **kargs)
|
||||
self.opts['pen'] = pen
|
||||
#self.curve.setPen(pen)
|
||||
#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
|
||||
emphacizing data).
|
||||
@ -148,10 +158,46 @@ class PlotDataItem(GraphicsObject):
|
||||
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 = fn.mkPen(*args, **kargs)
|
||||
self.opts['shadowPen'] = pen
|
||||
for c in self.curves:
|
||||
c.setPen(pen)
|
||||
self.update()
|
||||
#for c in self.curves:
|
||||
#c.setPen(pen)
|
||||
#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):
|
||||
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.
|
||||
"""
|
||||
|
||||
self.clear()
|
||||
#self.clear()
|
||||
|
||||
y = 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' 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'
|
||||
|
||||
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.yData = y.view(np.ndarray)
|
||||
self.xDisp = None
|
||||
self.yDisp = None
|
||||
|
||||
self.updateItems()
|
||||
view = self.getViewBox()
|
||||
@ -260,29 +308,37 @@ class PlotDataItem(GraphicsObject):
|
||||
|
||||
|
||||
def updateItems(self):
|
||||
for c in self.curves+self.scatters:
|
||||
if c.scene() is not None:
|
||||
c.scene().removeItem(c)
|
||||
#for c in self.curves+self.scatters:
|
||||
#if c.scene() is not None:
|
||||
#c.scene().removeItem(c)
|
||||
|
||||
curveArgs = {}
|
||||
for k in ['pen', 'shadowPen', 'fillLevel', 'brush']:
|
||||
curveArgs[k] = self.opts[k]
|
||||
|
||||
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]
|
||||
|
||||
x,y = self.getData()
|
||||
|
||||
self.curve.setData(x=x, y=y, **curveArgs)
|
||||
if curveArgs['pen'] is not None or curveArgs['brush'] is not None:
|
||||
curve = PlotCurveItem(x=x, y=y, **curveArgs)
|
||||
curve.setParentItem(self)
|
||||
self.curves.append(curve)
|
||||
self.curve.show()
|
||||
else:
|
||||
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:
|
||||
sp = ScatterPlotItem(x=x, y=y, **scatterArgs)
|
||||
sp.setParentItem(self)
|
||||
self.scatters.append(sp)
|
||||
self.scatter.show()
|
||||
else:
|
||||
self.scatter.hide()
|
||||
#sp = ScatterPlotItem(x=x, y=y, **scatterArgs)
|
||||
#sp.setParentItem(self)
|
||||
#self.scatters.append(sp)
|
||||
|
||||
|
||||
def getData(self):
|
||||
@ -335,15 +391,17 @@ class PlotDataItem(GraphicsObject):
|
||||
|
||||
|
||||
def clear(self):
|
||||
for i in self.curves+self.scatters:
|
||||
if i.scene() is not None:
|
||||
i.scene().removeItem(i)
|
||||
self.curves = []
|
||||
self.scatters = []
|
||||
#for i in self.curves+self.scatters:
|
||||
#if i.scene() is not None:
|
||||
#i.scene().removeItem(i)
|
||||
#self.curves = []
|
||||
#self.scatters = []
|
||||
self.xData = None
|
||||
self.yData = None
|
||||
self.xDisp = None
|
||||
self.yDisp = None
|
||||
self.curve.setData([])
|
||||
self.scatter.setData([])
|
||||
|
||||
def appendData(self, *args, **kargs):
|
||||
pass
|
||||
|
@ -2,6 +2,8 @@ from pyqtgraph.Qt import QtGui, QtCore
|
||||
from pyqtgraph.Point import Point
|
||||
import pyqtgraph.functions as fn
|
||||
from GraphicsObject import GraphicsObject
|
||||
import numpy as np
|
||||
import scipy.stats
|
||||
|
||||
__all__ = ['ScatterPlotItem', 'SpotItem']
|
||||
class ScatterPlotItem(GraphicsObject):
|
||||
@ -10,74 +12,265 @@ class ScatterPlotItem(GraphicsObject):
|
||||
sigClicked = QtCore.Signal(object, object) ## self, points
|
||||
sigPlotChanged = QtCore.Signal(object)
|
||||
|
||||
def __init__(self, spots=None, x=None, y=None, pxMode=True, pen='default', brush='default', size=7,
|
||||
symbol=None, identical=False, data=None):
|
||||
|
||||
def __init__(self, *args, **kargs):
|
||||
"""
|
||||
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.
|
||||
Accepts the same arguments as setData()
|
||||
"""
|
||||
|
||||
GraphicsObject.__init__(self)
|
||||
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.
|
||||
identical: If True, all spots are forced to look identical.
|
||||
Default is True
|
||||
*identical*: If True, all spots are forced to look identical.
|
||||
This can result in performance enhancement.
|
||||
|
||||
symbol can be one of:
|
||||
'o' circle
|
||||
Default is False
|
||||
*symbol* can be one (or a list) of:
|
||||
'o' circle (default)
|
||||
's' square
|
||||
't' triangle
|
||||
'd' diamond
|
||||
'+' 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
|
||||
## deal with non-keyword arguments
|
||||
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.')
|
||||
|
||||
if brush == 'default':
|
||||
self.brush = QtGui.QBrush(QtGui.QColor(100, 100, 150))
|
||||
## 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:
|
||||
x = []
|
||||
y = []
|
||||
for p in pos:
|
||||
if isinstance(p, QtCore.QPointF):
|
||||
x.append(p.x())
|
||||
y.append(p.y())
|
||||
else:
|
||||
x.append(p[0])
|
||||
y.append(p[1])
|
||||
kargs['x'] = x
|
||||
kargs['y'] = y
|
||||
|
||||
## determine how many spots we have
|
||||
if 'spots' in kargs:
|
||||
numPts = len(kargs['spots'])
|
||||
elif 'y' in kargs and kargs['y'] is not None:
|
||||
numPts = len(kargs['y'])
|
||||
else:
|
||||
self.brush = fn.mkBrush(brush)
|
||||
kargs['x'] = []
|
||||
kargs['y'] = []
|
||||
numPts = 0
|
||||
|
||||
if pen == 'default':
|
||||
self.pen = QtGui.QPen(QtGui.QColor(200, 200, 200))
|
||||
else:
|
||||
self.pen = fn.mkPen(pen)
|
||||
## create empty record array
|
||||
self.data = np.empty(numPts, dtype=[('x', float), ('y', float), ('size', float), ('symbol', 'S1'), ('pen', object), ('brush', object), ('data', object), ('spot', object)])
|
||||
self.data['size'] = -1 ## indicates use default size
|
||||
self.data['symbol'] = ''
|
||||
self.data['pen'] = None
|
||||
self.data['brush'] = None
|
||||
self.data['data'] = None
|
||||
|
||||
self.symbol = symbol
|
||||
self.size = size
|
||||
if 'spots' in kargs:
|
||||
spots = kargs['spots']
|
||||
for i in xrange(len(spots)):
|
||||
spot = spots[i]
|
||||
for k in spot:
|
||||
if k == 'pen':
|
||||
self.data[i][k] = fn.mkPen(spot[k])
|
||||
elif k == 'brush':
|
||||
self.data[i][k] = fn.mkBrush(spot[k])
|
||||
elif k == 'pos':
|
||||
pos = spot[k]
|
||||
if isinstance(pos, QtCore.QPointF):
|
||||
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']
|
||||
|
||||
self.pxMode = pxMode
|
||||
if spots is not None or x is not None:
|
||||
self.setPoints(spots, x, y, data)
|
||||
|
||||
#self.optimize = optimize
|
||||
#if optimize:
|
||||
#self.spotImage = QtGui.QImage(size, size, QtGui.QImage.Format_ARGB32_Premultiplied)
|
||||
#self.spotImage.fill(0)
|
||||
#p = QtGui.QPainter(self.spotImage)
|
||||
#p.setRenderHint(p.Antialiasing)
|
||||
#p.setBrush(brush)
|
||||
#p.setPen(pen)
|
||||
#p.drawEllipse(0, 0, size, size)
|
||||
#p.end()
|
||||
#self.optimizePixmap = QtGui.QPixmap(self.spotImage)
|
||||
#self.optimizeFragments = []
|
||||
#self.setFlags(self.flags() | self.ItemIgnoresTransformations)
|
||||
|
||||
## 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):
|
||||
ints = ['plotData']
|
||||
if interface is None:
|
||||
return 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):
|
||||
self.pxMode = mode
|
||||
|
||||
self.opts['pxMode'] = mode
|
||||
self.updateSpots()
|
||||
|
||||
def updateSpots(self):
|
||||
self.spotsValid = False
|
||||
self.update()
|
||||
|
||||
def clear(self):
|
||||
for i in self.spots:
|
||||
i.setParentItem(None)
|
||||
@ -85,73 +278,113 @@ class ScatterPlotItem(GraphicsObject):
|
||||
if s is not None:
|
||||
s.removeItem(i)
|
||||
self.spots = []
|
||||
self.data = None
|
||||
self.spotsValid = False
|
||||
self.bounds = [None, None]
|
||||
|
||||
|
||||
def getRange(self, ax, percent):
|
||||
return self.range[ax]
|
||||
def dataBounds(self, ax, frac=1.0):
|
||||
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):
|
||||
"""
|
||||
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.range = [[0,0],[0,0]]
|
||||
self.addPoints(spots, x, y, data)
|
||||
|
||||
def addPoints(self, spots=None, x=None, y=None, data=None):
|
||||
xmn = ymn = xmx = ymx = None
|
||||
if spots is not None:
|
||||
n = len(spots)
|
||||
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:
|
||||
n = len(x)
|
||||
return (scipy.stats.scoreatpercentile(d, 50 - (frac * 50)), scipy.stats.scoreatpercentile(d, 50 + (frac * 50)))
|
||||
|
||||
|
||||
|
||||
|
||||
def addPoints(self, *args, **kargs):
|
||||
"""
|
||||
Add new points to the scatter plot.
|
||||
Arguments are the same as setData()
|
||||
Note: this is expensive; plenty of room for optimization here.
|
||||
"""
|
||||
if self.data is None:
|
||||
self.setData(*args, **kargs)
|
||||
return
|
||||
|
||||
|
||||
for i in range(n):
|
||||
if spots is not None:
|
||||
s = spots[i]
|
||||
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)
|
||||
if self.pxMode:
|
||||
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
|
||||
|
||||
## apply defaults
|
||||
size = self.data['size'].copy()
|
||||
size[size<0] = self.opts['size']
|
||||
|
||||
pen = self.data['pen'].copy()
|
||||
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
|
||||
else:
|
||||
psize = size
|
||||
if xmn is None:
|
||||
xmn = pos[0]-psize
|
||||
xmx = pos[0]+psize
|
||||
ymn = pos[1]-psize
|
||||
ymx = pos[1]+psize
|
||||
else:
|
||||
xmn = min(xmn, pos[0]-psize)
|
||||
xmx = max(xmx, pos[0]+psize)
|
||||
ymn = min(ymn, pos[1]-psize)
|
||||
ymx = max(ymx, pos[1]+psize)
|
||||
#print pos, xmn, xmx, ymn, ymx
|
||||
brush = s.get('brush', self.brush)
|
||||
pen = s.get('pen', self.pen)
|
||||
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))
|
||||
psize = size[i]
|
||||
|
||||
#if xmn is None:
|
||||
#xmn = pos[0]-psize
|
||||
#xmx = pos[0]+psize
|
||||
#ymn = pos[1]-psize
|
||||
#ymx = pos[1]+psize
|
||||
#else:
|
||||
#xmn = min(xmn, pos[0]-psize)
|
||||
#xmx = max(xmx, pos[0]+psize)
|
||||
#ymn = min(ymn, pos[1]-psize)
|
||||
#ymx = max(ymx, pos[1]+psize)
|
||||
|
||||
item = self.mkSpot(pos, size[i], self.opts['pxMode'], brush[i], pen[i], s['data'], symbol=symbol[i], index=len(self.spots))
|
||||
self.spots.append(item)
|
||||
self.data[i]['spot'] = item
|
||||
#if self.optimize:
|
||||
#item.hide()
|
||||
#frag = QtGui.QPainter.PixmapFragment.create(pos, QtCore.QRectF(0, 0, size, size))
|
||||
#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):
|
||||
#for s in self.spots:
|
||||
@ -166,24 +399,22 @@ class ScatterPlotItem(GraphicsObject):
|
||||
#p.drawPixmapFragments(self.optimizeFragments, self.optimizePixmap)
|
||||
|
||||
def paint(self, *args):
|
||||
pass
|
||||
if not self.spotsValid:
|
||||
self.generateSpots()
|
||||
|
||||
def spotPixmap(self):
|
||||
## If all spots are identical, return the pixmap to use for all spots
|
||||
## Otherwise return None
|
||||
|
||||
if not self.identical:
|
||||
if not self.opts['identical']:
|
||||
return None
|
||||
if self._spotPixmap is None:
|
||||
#print 'spotPixmap'
|
||||
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)
|
||||
spot = SpotItem(size=self.opts['size'], pxMode=True, brush=self.opts['brush'], pen=self.opts['pen'], symbol=self.opts['symbol'])
|
||||
self._spotPixmap = spot.pixmap
|
||||
return self._spotPixmap
|
||||
|
||||
def mkSpot(self, pos, size, pxMode, brush, pen, data, symbol=None, index=None):
|
||||
## Make and return a SpotItem (or PixmapSpotItem if in pxMode)
|
||||
|
||||
brush = fn.mkBrush(brush)
|
||||
pen = fn.mkPen(pen)
|
||||
if pxMode:
|
||||
@ -198,10 +429,19 @@ class ScatterPlotItem(GraphicsObject):
|
||||
return item
|
||||
|
||||
def boundingRect(self):
|
||||
((xmn, xmx), (ymn, ymx)) = self.range
|
||||
if xmn is None or xmx is None or ymn is None or ymx is None:
|
||||
return QtCore.QRectF()
|
||||
(xmn, xmx) = self.dataBounds(ax=0)
|
||||
(ymn, ymx) = self.dataBounds(ax=1)
|
||||
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)
|
||||
|
||||
#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)
|
||||
|
||||
#def pointClicked(self, point):
|
||||
@ -222,7 +462,7 @@ class ScatterPlotItem(GraphicsObject):
|
||||
sx = sp.x()
|
||||
sy = sp.y()
|
||||
s2x = s2y = ss * 0.5
|
||||
if self.pxMode:
|
||||
if self.opts['pxMode']:
|
||||
s2x *= pw
|
||||
s2y *= ph
|
||||
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)
|
||||
self.pxMode = pxMode
|
||||
|
||||
try:
|
||||
symbol = int(symbol)
|
||||
except:
|
||||
pass
|
||||
|
||||
if symbol is None:
|
||||
symbol = 'o' ## circle by default
|
||||
elif isinstance(symbol, int): ## allow symbols specified by integer for easy iteration
|
||||
symbol = ['o', 's', 't', 'd', '+'][symbol]
|
||||
|
||||
|
||||
|
||||
####print 'SpotItem symbol: ', symbol
|
||||
self.data = data
|
||||
self.pen = pen
|
||||
@ -294,11 +542,12 @@ class SpotItem(GraphicsObject):
|
||||
self.symbol = symbol
|
||||
#s2 = size/2.
|
||||
self.path = QtGui.QPainterPath()
|
||||
|
||||
if symbol == 'o':
|
||||
self.path.addEllipse(QtCore.QRectF(-0.5, -0.5, 1, 1))
|
||||
elif symbol == 's':
|
||||
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.lineTo(0, 0.5)
|
||||
self.path.lineTo(0.5, -0.5)
|
||||
@ -328,7 +577,7 @@ class SpotItem(GraphicsObject):
|
||||
#self.path.connectPath(self.path)
|
||||
#elif symbol == 'x':
|
||||
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))
|
||||
|
||||
if pxMode:
|
||||
|
Loading…
Reference in New Issue
Block a user