More boundingRect / dataBounds bugfixes
This commit is contained in:
parent
93a5753f5d
commit
9f55a27fdd
@ -70,7 +70,7 @@ s3 = pg.ScatterPlotItem(pxMode=False) ## Set pxMode=False to allow spots to tr
|
||||
spots3 = []
|
||||
for i in range(10):
|
||||
for j in range(10):
|
||||
spots3.append({'pos': (1e-6*i, 1e-6*j), 'size': 1e-6, 'pen': {'color': 'w', 'width': 8}, 'brush':pg.intColor(i*10+j, 100)})
|
||||
spots3.append({'pos': (1e-6*i, 1e-6*j), 'size': 1e-6, 'pen': {'color': 'w', 'width': 2}, 'brush':pg.intColor(i*10+j, 100)})
|
||||
s3.addPoints(spots3)
|
||||
w3.addItem(s3)
|
||||
s3.sigClicked.connect(clicked)
|
||||
|
@ -93,7 +93,7 @@ class PlotCurveItem(GraphicsObject):
|
||||
|
||||
(x, y) = self.getData()
|
||||
if x is None or len(x) == 0:
|
||||
return (0, 0)
|
||||
return (None, None)
|
||||
|
||||
if ax == 0:
|
||||
d = x
|
||||
@ -102,20 +102,106 @@ class PlotCurveItem(GraphicsObject):
|
||||
d = y
|
||||
d2 = x
|
||||
|
||||
## If an orthogonal range is specified, mask the data now
|
||||
if orthoRange is not None:
|
||||
mask = (d2 >= orthoRange[0]) * (d2 <= orthoRange[1])
|
||||
d = d[mask]
|
||||
d2 = d2[mask]
|
||||
|
||||
|
||||
## Get min/max (or percentiles) of the requested data range
|
||||
if frac >= 1.0:
|
||||
b = (d.min(), d.max())
|
||||
elif frac <= 0.0:
|
||||
raise Exception("Value for parameter 'frac' must be > 0. (got %s)" % str(frac))
|
||||
else:
|
||||
b = (scipy.stats.scoreatpercentile(d, 50 - (frac * 50)), scipy.stats.scoreatpercentile(d, 50 + (frac * 50)))
|
||||
|
||||
## adjust for fill level
|
||||
if ax == 1 and self.opts['fillLevel'] is not None:
|
||||
b = (min(b[0], self.opts['fillLevel']), max(b[1], self.opts['fillLevel']))
|
||||
|
||||
## Add pen width only if it is non-cosmetic.
|
||||
pen = self.opts['pen']
|
||||
spen = self.opts['shadowPen']
|
||||
if not pen.isCosmetic():
|
||||
b = (b[0] - pen.widthF()*0.7072, b[1] + pen.widthF()*0.7072)
|
||||
if spen is not None and not spen.isCosmetic() and spen.style() != QtCore.Qt.NoPen:
|
||||
b = (b[0] - spen.widthF()*0.7072, b[1] + spen.widthF()*0.7072)
|
||||
|
||||
self._boundsCache[ax] = [(frac, orthoRange), b]
|
||||
return b
|
||||
|
||||
def pixelPadding(self):
|
||||
pen = self.opts['pen']
|
||||
spen = self.opts['shadowPen']
|
||||
w = 0
|
||||
if pen.isCosmetic():
|
||||
w += pen.widthF()*0.7072
|
||||
if spen is not None and spen.isCosmetic() and spen.style() != QtCore.Qt.NoPen:
|
||||
w = max(w, spen.widthF()*0.7072)
|
||||
return w
|
||||
|
||||
def boundingRect(self):
|
||||
if self._boundingRect is None:
|
||||
(xmn, xmx) = self.dataBounds(ax=0)
|
||||
(ymn, ymx) = self.dataBounds(ax=1)
|
||||
if xmn is None:
|
||||
return QtCore.QRectF()
|
||||
|
||||
px = py = 0.0
|
||||
pxPad = self.pixelPadding()
|
||||
if pxPad > 0:
|
||||
# determine length of pixel in local x, y directions
|
||||
px, py = self.pixelVectors()
|
||||
px = 0 if px is None else px.length()
|
||||
py = 0 if py is None else py.length()
|
||||
|
||||
# return bounds expanded by pixel size
|
||||
px *= pxPad
|
||||
py *= pxPad
|
||||
#px += self._maxSpotWidth * 0.5
|
||||
#py += self._maxSpotWidth * 0.5
|
||||
self._boundingRect = QtCore.QRectF(xmn-px, ymn-py, (2*px)+xmx-xmn, (2*py)+ymx-ymn)
|
||||
return self._boundingRect
|
||||
|
||||
def viewTransformChanged(self):
|
||||
self.invalidateBounds()
|
||||
self.prepareGeometryChange()
|
||||
|
||||
#def boundingRect(self):
|
||||
#if self._boundingRect is None:
|
||||
#(x, y) = self.getData()
|
||||
#if x is None or y is None or len(x) == 0 or len(y) == 0:
|
||||
#return QtCore.QRectF()
|
||||
|
||||
|
||||
#if self.opts['shadowPen'] is not None:
|
||||
#lineWidth = (max(self.opts['pen'].width(), self.opts['shadowPen'].width()) + 1)
|
||||
#else:
|
||||
#lineWidth = (self.opts['pen'].width()+1)
|
||||
|
||||
|
||||
#pixels = self.pixelVectors()
|
||||
#if pixels == (None, None):
|
||||
#pixels = [Point(0,0), Point(0,0)]
|
||||
|
||||
#xmin = x.min()
|
||||
#xmax = x.max()
|
||||
#ymin = y.min()
|
||||
#ymax = y.max()
|
||||
|
||||
#if self.opts['fillLevel'] is not None:
|
||||
#ymin = min(ymin, self.opts['fillLevel'])
|
||||
#ymax = max(ymax, self.opts['fillLevel'])
|
||||
|
||||
#xmin -= pixels[0].x() * lineWidth
|
||||
#xmax += pixels[0].x() * lineWidth
|
||||
#ymin -= abs(pixels[1].y()) * lineWidth
|
||||
#ymax += abs(pixels[1].y()) * lineWidth
|
||||
|
||||
#self._boundingRect = QtCore.QRectF(xmin, ymin, xmax-xmin, ymax-ymin)
|
||||
#return self._boundingRect
|
||||
|
||||
|
||||
def invalidateBounds(self):
|
||||
self._boundingRect = None
|
||||
@ -280,40 +366,6 @@ class PlotCurveItem(GraphicsObject):
|
||||
return QtGui.QPainterPath()
|
||||
return self.path
|
||||
|
||||
def boundingRect(self):
|
||||
if self._boundingRect is None:
|
||||
(x, y) = self.getData()
|
||||
if x is None or y is None or len(x) == 0 or len(y) == 0:
|
||||
return QtCore.QRectF()
|
||||
|
||||
|
||||
if self.opts['shadowPen'] is not None:
|
||||
lineWidth = (max(self.opts['pen'].width(), self.opts['shadowPen'].width()) + 1)
|
||||
else:
|
||||
lineWidth = (self.opts['pen'].width()+1)
|
||||
|
||||
|
||||
pixels = self.pixelVectors()
|
||||
if pixels == (None, None):
|
||||
pixels = [Point(0,0), Point(0,0)]
|
||||
|
||||
xmin = x.min()
|
||||
xmax = x.max()
|
||||
ymin = y.min()
|
||||
ymax = y.max()
|
||||
|
||||
if self.opts['fillLevel'] is not None:
|
||||
ymin = min(ymin, self.opts['fillLevel'])
|
||||
ymax = max(ymax, self.opts['fillLevel'])
|
||||
|
||||
xmin -= pixels[0].x() * lineWidth
|
||||
xmax += pixels[0].x() * lineWidth
|
||||
ymin -= abs(pixels[1].y()) * lineWidth
|
||||
ymax += abs(pixels[1].y()) * lineWidth
|
||||
|
||||
self._boundingRect = QtCore.QRectF(xmin, ymin, xmax-xmin, ymax-ymin)
|
||||
return self._boundingRect
|
||||
|
||||
def paint(self, p, opt, widget):
|
||||
prof = debug.Profiler('PlotCurveItem.paint '+str(id(self)), disabled=True)
|
||||
if self.xData is None:
|
||||
|
@ -471,33 +471,57 @@ class PlotDataItem(GraphicsObject):
|
||||
and max)
|
||||
=============== =============================================================
|
||||
"""
|
||||
if frac <= 0.0:
|
||||
raise Exception("Value for parameter 'frac' must be > 0. (got %s)" % str(frac))
|
||||
|
||||
(x, y) = self.getData()
|
||||
if x is None or len(x) == 0:
|
||||
return None
|
||||
range = [None, None]
|
||||
if self.curve.isVisible():
|
||||
range = self.curve.dataBounds(ax, frac, orthoRange)
|
||||
elif self.scatter.isVisible():
|
||||
r2 = self.scatter.dataBounds(ax, frac, orthoRange)
|
||||
range = [
|
||||
r2[0] if range[0] is None else (range[0] if r2[0] is None else min(r2[0], range[0])),
|
||||
r2[1] if range[1] is None else (range[1] if r2[1] is None else min(r2[1], range[1]))
|
||||
]
|
||||
return range
|
||||
|
||||
#if frac <= 0.0:
|
||||
#raise Exception("Value for parameter 'frac' must be > 0. (got %s)" % str(frac))
|
||||
|
||||
#(x, y) = self.getData()
|
||||
#if x is None or len(x) == 0:
|
||||
#return None
|
||||
|
||||
if ax == 0:
|
||||
d = x
|
||||
d2 = y
|
||||
elif ax == 1:
|
||||
d = y
|
||||
d2 = x
|
||||
#if ax == 0:
|
||||
#d = x
|
||||
#d2 = y
|
||||
#elif ax == 1:
|
||||
#d = y
|
||||
#d2 = x
|
||||
|
||||
if orthoRange is not None:
|
||||
mask = (d2 >= orthoRange[0]) * (d2 <= orthoRange[1])
|
||||
d = d[mask]
|
||||
#d2 = d2[mask]
|
||||
#if orthoRange is not None:
|
||||
#mask = (d2 >= orthoRange[0]) * (d2 <= orthoRange[1])
|
||||
#d = d[mask]
|
||||
##d2 = d2[mask]
|
||||
|
||||
if len(d) > 0:
|
||||
if frac >= 1.0:
|
||||
return (np.min(d), np.max(d))
|
||||
else:
|
||||
return (scipy.stats.scoreatpercentile(d, 50 - (frac * 50)), scipy.stats.scoreatpercentile(d, 50 + (frac * 50)))
|
||||
else:
|
||||
return None
|
||||
|
||||
#if len(d) > 0:
|
||||
#if frac >= 1.0:
|
||||
#return (np.min(d), np.max(d))
|
||||
#else:
|
||||
#return (scipy.stats.scoreatpercentile(d, 50 - (frac * 50)), scipy.stats.scoreatpercentile(d, 50 + (frac * 50)))
|
||||
#else:
|
||||
#return None
|
||||
|
||||
def pixelPadding(self):
|
||||
"""
|
||||
Return the size in pixels that this item may draw beyond the values returned by dataBounds().
|
||||
This method is called by ViewBox when auto-scaling.
|
||||
"""
|
||||
pad = 0
|
||||
if self.curve.isVisible():
|
||||
pad = max(pad, self.curve.pixelPadding())
|
||||
elif self.scatter.isVisible():
|
||||
pad = max(pad, self.scatter.pixelPadding())
|
||||
return pad
|
||||
|
||||
|
||||
def clear(self):
|
||||
#for i in self.curves+self.scatters:
|
||||
|
@ -60,7 +60,7 @@ def renderSymbol(symbol, size, pen, brush, device=None):
|
||||
#return SymbolPixmapCache[key]
|
||||
|
||||
## Render a spot with the given parameters to a pixmap
|
||||
penPxWidth = max(np.ceil(pen.width()), 1)
|
||||
penPxWidth = max(np.ceil(pen.widthF()), 1)
|
||||
image = QtGui.QImage(int(size+penPxWidth), int(size+penPxWidth), QtGui.QImage.Format_ARGB32)
|
||||
image.fill(0)
|
||||
p = QtGui.QPainter(image)
|
||||
@ -115,7 +115,7 @@ class SymbolAtlas(object):
|
||||
symbol, size, pen, brush = rec['symbol'], rec['size'], rec['pen'], rec['brush']
|
||||
pen = fn.mkPen(pen) if not isinstance(pen, QtGui.QPen) else pen
|
||||
brush = fn.mkBrush(brush) if not isinstance(pen, QtGui.QBrush) else brush
|
||||
key = (symbol, size, fn.colorTuple(pen.color()), pen.width(), pen.style(), fn.colorTuple(brush.color()))
|
||||
key = (symbol, size, fn.colorTuple(pen.color()), pen.widthF(), pen.style(), fn.colorTuple(brush.color()))
|
||||
if key not in self.symbolMap:
|
||||
newCoords = SymbolAtlas.SymbolCoords()
|
||||
self.symbolMap[key] = newCoords
|
||||
@ -589,13 +589,13 @@ class ScatterPlotItem(GraphicsObject):
|
||||
width = 0
|
||||
pxWidth = 0
|
||||
if self.opts['pxMode']:
|
||||
pxWidth = size + pen.width()
|
||||
pxWidth = size + pen.widthF()
|
||||
else:
|
||||
width = size
|
||||
if pen.isCosmetic():
|
||||
pxWidth += pen.width()
|
||||
pxWidth += pen.widthF()
|
||||
else:
|
||||
width += pen.width()
|
||||
width += pen.widthF()
|
||||
self._maxSpotWidth = max(self._maxSpotWidth, width)
|
||||
self._maxSpotPxWidth = max(self._maxSpotPxWidth, pxWidth)
|
||||
self.bounds = [None, None]
|
||||
@ -629,20 +629,7 @@ class ScatterPlotItem(GraphicsObject):
|
||||
d2 = d2[mask]
|
||||
|
||||
if frac >= 1.0:
|
||||
## increase size of bounds based on spot size and pen width
|
||||
#px = self.pixelLength(Point(1, 0) if ax == 0 else Point(0, 1)) ## determine length of pixel along this axis
|
||||
#px = self.pixelVectors()[ax]
|
||||
#if px is None:
|
||||
#px = 0
|
||||
#else:
|
||||
#px = px.length()
|
||||
#minIndex = np.argmin(d)
|
||||
#maxIndex = np.argmax(d)
|
||||
#minVal = d[minIndex]
|
||||
#maxVal = d[maxIndex]
|
||||
#spotSize = 0.5 * (self._maxSpotWidth + px * self._maxSpotPxWidth)
|
||||
#self.bounds[ax] = (minVal-spotSize, maxVal+spotSize)
|
||||
self.bounds[ax] = (d.min() - 0.5*self._maxSpotWidth, d.max() + 0.5*self._maxSpotWidth)
|
||||
self.bounds[ax] = (d.min() - self._maxSpotWidth*0.7072, d.max() + self._maxSpotWidth*0.7072)
|
||||
return self.bounds[ax]
|
||||
elif frac <= 0.0:
|
||||
raise Exception("Value for parameter 'frac' must be > 0. (got %s)" % str(frac))
|
||||
@ -650,13 +637,7 @@ class ScatterPlotItem(GraphicsObject):
|
||||
return (scipy.stats.scoreatpercentile(d, 50 - (frac * 50)), scipy.stats.scoreatpercentile(d, 50 + (frac * 50)))
|
||||
|
||||
def pixelPadding(self):
|
||||
return self._maxSpotPxWidth
|
||||
|
||||
#def defaultSpotPixmap(self):
|
||||
### Return the default spot pixmap
|
||||
#if self._spotPixmap is None:
|
||||
#self._spotPixmap = makeSymbolPixmap(size=self.opts['size'], brush=self.opts['brush'], pen=self.opts['pen'], symbol=self.opts['symbol'])
|
||||
#return self._spotPixmap
|
||||
return self._maxSpotPxWidth*0.7072
|
||||
|
||||
def boundingRect(self):
|
||||
(xmn, xmx) = self.dataBounds(ax=0)
|
||||
@ -669,17 +650,16 @@ class ScatterPlotItem(GraphicsObject):
|
||||
ymx = 0
|
||||
|
||||
px = py = 0.0
|
||||
if self._maxSpotPxWidth > 0:
|
||||
pxPad = self.pixelPadding()
|
||||
if pxPad > 0:
|
||||
# determine length of pixel in local x, y directions
|
||||
px, py = self.pixelVectors()
|
||||
px = 0 if px is None else px.length() * 0.5
|
||||
py = 0 if py is None else py.length() * 0.5
|
||||
px = 0 if px is None else px.length()
|
||||
py = 0 if py is None else py.length()
|
||||
|
||||
# return bounds expanded by pixel size
|
||||
px *= self._maxSpotPxWidth
|
||||
py *= self._maxSpotPxWidth
|
||||
px += self._maxSpotWidth * 0.5
|
||||
py += self._maxSpotWidth * 0.5
|
||||
px *= pxPad
|
||||
py *= pxPad
|
||||
return QtCore.QRectF(xmn-px, ymn-py, (2*px)+xmx-xmn, (2*py)+ymx-ymn)
|
||||
|
||||
def viewTransformChanged(self):
|
||||
|
Loading…
Reference in New Issue
Block a user