avoid double call to mkPen when creating PlotCurveItem objects (#817)

* avoid double call to mkPen when creating PlotCurveItem objects

* avoid unnecessary calls to mkPen in paint
This commit is contained in:
Daniel Hrisca 2019-06-24 03:30:40 +03:00 committed by Ogi Moore
parent 3e7cace746
commit 297e1d95a5

View File

@ -4,7 +4,7 @@ try:
HAVE_OPENGL = True HAVE_OPENGL = True
except: except:
HAVE_OPENGL = False HAVE_OPENGL = False
import numpy as np import numpy as np
from .GraphicsObject import GraphicsObject from .GraphicsObject import GraphicsObject
from .. import functions as fn from .. import functions as fn
@ -15,51 +15,50 @@ from .. import debug
__all__ = ['PlotCurveItem'] __all__ = ['PlotCurveItem']
class PlotCurveItem(GraphicsObject): class PlotCurveItem(GraphicsObject):
""" """
Class representing a single plot curve. Instances of this class are created Class representing a single plot curve. Instances of this class are created
automatically as part of PlotDataItem; these rarely need to be instantiated automatically as part of PlotDataItem; these rarely need to be instantiated
directly. directly.
Features: Features:
- Fast data update - Fast data update
- Fill under curve - Fill under curve
- Mouse interaction - Mouse interaction
==================== =============================================== ==================== ===============================================
**Signals:** **Signals:**
sigPlotChanged(self) Emitted when the data being plotted has changed sigPlotChanged(self) Emitted when the data being plotted has changed
sigClicked(self) Emitted when the curve is clicked sigClicked(self) Emitted when the curve is clicked
==================== =============================================== ==================== ===============================================
""" """
sigPlotChanged = QtCore.Signal(object) sigPlotChanged = QtCore.Signal(object)
sigClicked = QtCore.Signal(object) sigClicked = QtCore.Signal(object)
def __init__(self, *args, **kargs): def __init__(self, *args, **kargs):
""" """
Forwards all arguments to :func:`setData <pyqtgraph.PlotCurveItem.setData>`. Forwards all arguments to :func:`setData <pyqtgraph.PlotCurveItem.setData>`.
Some extra arguments are accepted as well: Some extra arguments are accepted as well:
============== ======================================================= ============== =======================================================
**Arguments:** **Arguments:**
parent The parent GraphicsObject (optional) parent The parent GraphicsObject (optional)
clickable If True, the item will emit sigClicked when it is clickable If True, the item will emit sigClicked when it is
clicked on. Defaults to False. clicked on. Defaults to False.
============== ======================================================= ============== =======================================================
""" """
GraphicsObject.__init__(self, kargs.get('parent', None)) GraphicsObject.__init__(self, kargs.get('parent', None))
self.clear() self.clear()
## this is disastrous for performance. ## this is disastrous for performance.
#self.setCacheMode(QtGui.QGraphicsItem.DeviceCoordinateCache) #self.setCacheMode(QtGui.QGraphicsItem.DeviceCoordinateCache)
self.metaData = {} self.metaData = {}
self.opts = { self.opts = {
'pen': fn.mkPen('w'),
'shadowPen': None, 'shadowPen': None,
'fillLevel': None, 'fillLevel': None,
'brush': None, 'brush': None,
@ -70,21 +69,23 @@ class PlotCurveItem(GraphicsObject):
'mouseWidth': 8, # width of shape responding to mouse click 'mouseWidth': 8, # width of shape responding to mouse click
'compositionMode': None, 'compositionMode': None,
} }
if 'pen' not in kargs:
self.opts['pen'] = fn.mkPen('w')
self.setClickable(kargs.get('clickable', False)) self.setClickable(kargs.get('clickable', False))
self.setData(*args, **kargs) self.setData(*args, **kargs)
def implements(self, interface=None): def implements(self, interface=None):
ints = ['plotData'] ints = ['plotData']
if interface is None: if interface is None:
return ints return ints
return interface in ints return interface in ints
def name(self): def name(self):
return self.opts.get('name', None) return self.opts.get('name', None)
def setClickable(self, s, width=None): def setClickable(self, s, width=None):
"""Sets whether the item responds to mouse clicks. """Sets whether the item responds to mouse clicks.
The *width* argument specifies the width in pixels orthogonal to the The *width* argument specifies the width in pixels orthogonal to the
curve that will respond to a mouse click. curve that will respond to a mouse click.
""" """
@ -92,41 +93,41 @@ class PlotCurveItem(GraphicsObject):
if width is not None: if width is not None:
self.opts['mouseWidth'] = width self.opts['mouseWidth'] = width
self._mouseShape = None self._mouseShape = None
self._boundingRect = None self._boundingRect = None
def setCompositionMode(self, mode): def setCompositionMode(self, mode):
"""Change the composition mode of the item (see QPainter::CompositionMode """Change the composition mode of the item (see QPainter::CompositionMode
in the Qt documentation). This is useful when overlaying multiple items. in the Qt documentation). This is useful when overlaying multiple items.
============================================ ============================================================ ============================================ ============================================================
**Most common arguments:** **Most common arguments:**
QtGui.QPainter.CompositionMode_SourceOver Default; image replaces the background if it QtGui.QPainter.CompositionMode_SourceOver Default; image replaces the background if it
is opaque. Otherwise, it uses the alpha channel to blend is opaque. Otherwise, it uses the alpha channel to blend
the image with the background. the image with the background.
QtGui.QPainter.CompositionMode_Overlay The image color is mixed with the background color to QtGui.QPainter.CompositionMode_Overlay The image color is mixed with the background color to
reflect the lightness or darkness of the background. reflect the lightness or darkness of the background.
QtGui.QPainter.CompositionMode_Plus Both the alpha and color of the image and background pixels QtGui.QPainter.CompositionMode_Plus Both the alpha and color of the image and background pixels
are added together. are added together.
QtGui.QPainter.CompositionMode_Multiply The output is the image color multiplied by the background. QtGui.QPainter.CompositionMode_Multiply The output is the image color multiplied by the background.
============================================ ============================================================ ============================================ ============================================================
""" """
self.opts['compositionMode'] = mode self.opts['compositionMode'] = mode
self.update() self.update()
def getData(self): def getData(self):
return self.xData, self.yData return self.xData, self.yData
def dataBounds(self, ax, frac=1.0, orthoRange=None): def dataBounds(self, ax, frac=1.0, orthoRange=None):
## Need this to run as fast as possible. ## Need this to run as fast as possible.
## check cache first: ## check cache first:
cache = self._boundsCache[ax] cache = self._boundsCache[ax]
if cache is not None and cache[0] == (frac, orthoRange): if cache is not None and cache[0] == (frac, orthoRange):
return cache[1] return cache[1]
(x, y) = self.getData() (x, y) = self.getData()
if x is None or len(x) == 0: if x is None or len(x) == 0:
return (None, None) return (None, None)
if ax == 0: if ax == 0:
d = x d = x
d2 = y d2 = y
@ -139,7 +140,7 @@ class PlotCurveItem(GraphicsObject):
mask = (d2 >= orthoRange[0]) * (d2 <= orthoRange[1]) mask = (d2 >= orthoRange[0]) * (d2 <= orthoRange[1])
d = d[mask] d = d[mask]
#d2 = d2[mask] #d2 = d2[mask]
if len(d) == 0: if len(d) == 0:
return (None, None) return (None, None)
@ -154,7 +155,7 @@ class PlotCurveItem(GraphicsObject):
if len(d) == 0: if len(d) == 0:
return (None, None) return (None, None)
b = (d.min(), d.max()) b = (d.min(), d.max())
elif frac <= 0.0: elif frac <= 0.0:
raise Exception("Value for parameter 'frac' must be > 0. (got %s)" % str(frac)) raise Exception("Value for parameter 'frac' must be > 0. (got %s)" % str(frac))
else: else:
@ -166,7 +167,7 @@ class PlotCurveItem(GraphicsObject):
## adjust for fill level ## adjust for fill level
if ax == 1 and self.opts['fillLevel'] is not None: if ax == 1 and self.opts['fillLevel'] is not None:
b = (min(b[0], self.opts['fillLevel']), max(b[1], self.opts['fillLevel'])) b = (min(b[0], self.opts['fillLevel']), max(b[1], self.opts['fillLevel']))
## Add pen width only if it is non-cosmetic. ## Add pen width only if it is non-cosmetic.
pen = self.opts['pen'] pen = self.opts['pen']
spen = self.opts['shadowPen'] spen = self.opts['shadowPen']
@ -174,10 +175,10 @@ class PlotCurveItem(GraphicsObject):
b = (b[0] - pen.widthF()*0.7072, b[1] + pen.widthF()*0.7072) 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: 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) b = (b[0] - spen.widthF()*0.7072, b[1] + spen.widthF()*0.7072)
self._boundsCache[ax] = [(frac, orthoRange), b] self._boundsCache[ax] = [(frac, orthoRange), b]
return b return b
def pixelPadding(self): def pixelPadding(self):
pen = self.opts['pen'] pen = self.opts['pen']
spen = self.opts['shadowPen'] spen = self.opts['shadowPen']
@ -196,11 +197,11 @@ class PlotCurveItem(GraphicsObject):
(ymn, ymx) = self.dataBounds(ax=1) (ymn, ymx) = self.dataBounds(ax=1)
if xmn is None or ymn is None: if xmn is None or ymn is None:
return QtCore.QRectF() return QtCore.QRectF()
px = py = 0.0 px = py = 0.0
pxPad = self.pixelPadding() pxPad = self.pixelPadding()
if pxPad > 0: if pxPad > 0:
# determine length of pixel in local x, y directions # determine length of pixel in local x, y directions
px, py = self.pixelVectors() px, py = self.pixelVectors()
try: try:
px = 0 if px is None else px.length() px = 0 if px is None else px.length()
@ -210,68 +211,68 @@ class PlotCurveItem(GraphicsObject):
py = 0 if py is None else py.length() py = 0 if py is None else py.length()
except OverflowError: except OverflowError:
py = 0 py = 0
# return bounds expanded by pixel size # return bounds expanded by pixel size
px *= pxPad px *= pxPad
py *= pxPad py *= pxPad
#px += self._maxSpotWidth * 0.5 #px += self._maxSpotWidth * 0.5
#py += 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) self._boundingRect = QtCore.QRectF(xmn-px, ymn-py, (2*px)+xmx-xmn, (2*py)+ymx-ymn)
return self._boundingRect return self._boundingRect
def viewTransformChanged(self): def viewTransformChanged(self):
self.invalidateBounds() self.invalidateBounds()
self.prepareGeometryChange() self.prepareGeometryChange()
#def boundingRect(self): #def boundingRect(self):
#if self._boundingRect is None: #if self._boundingRect is None:
#(x, y) = self.getData() #(x, y) = self.getData()
#if x is None or y is None or len(x) == 0 or len(y) == 0: #if x is None or y is None or len(x) == 0 or len(y) == 0:
#return QtCore.QRectF() #return QtCore.QRectF()
#if self.opts['shadowPen'] is not None: #if self.opts['shadowPen'] is not None:
#lineWidth = (max(self.opts['pen'].width(), self.opts['shadowPen'].width()) + 1) #lineWidth = (max(self.opts['pen'].width(), self.opts['shadowPen'].width()) + 1)
#else: #else:
#lineWidth = (self.opts['pen'].width()+1) #lineWidth = (self.opts['pen'].width()+1)
#pixels = self.pixelVectors() #pixels = self.pixelVectors()
#if pixels == (None, None): #if pixels == (None, None):
#pixels = [Point(0,0), Point(0,0)] #pixels = [Point(0,0), Point(0,0)]
#xmin = x.min() #xmin = x.min()
#xmax = x.max() #xmax = x.max()
#ymin = y.min() #ymin = y.min()
#ymax = y.max() #ymax = y.max()
#if self.opts['fillLevel'] is not None: #if self.opts['fillLevel'] is not None:
#ymin = min(ymin, self.opts['fillLevel']) #ymin = min(ymin, self.opts['fillLevel'])
#ymax = max(ymax, self.opts['fillLevel']) #ymax = max(ymax, self.opts['fillLevel'])
#xmin -= pixels[0].x() * lineWidth #xmin -= pixels[0].x() * lineWidth
#xmax += pixels[0].x() * lineWidth #xmax += pixels[0].x() * lineWidth
#ymin -= abs(pixels[1].y()) * lineWidth #ymin -= abs(pixels[1].y()) * lineWidth
#ymax += abs(pixels[1].y()) * lineWidth #ymax += abs(pixels[1].y()) * lineWidth
#self._boundingRect = QtCore.QRectF(xmin, ymin, xmax-xmin, ymax-ymin) #self._boundingRect = QtCore.QRectF(xmin, ymin, xmax-xmin, ymax-ymin)
#return self._boundingRect #return self._boundingRect
def invalidateBounds(self): def invalidateBounds(self):
self._boundingRect = None self._boundingRect = None
self._boundsCache = [None, None] self._boundsCache = [None, None]
def setPen(self, *args, **kargs): def setPen(self, *args, **kargs):
"""Set the pen used to draw the curve.""" """Set the pen used to draw the curve."""
self.opts['pen'] = fn.mkPen(*args, **kargs) self.opts['pen'] = fn.mkPen(*args, **kargs)
self.invalidateBounds() self.invalidateBounds()
self.update() self.update()
def setShadowPen(self, *args, **kargs): def setShadowPen(self, *args, **kargs):
"""Set the shadow pen used to draw behind tyhe primary pen. """Set the shadow pen used to draw behind tyhe primary pen.
This pen must have a larger width than the primary This pen must have a larger width than the primary
pen to be visible. pen to be visible.
""" """
self.opts['shadowPen'] = fn.mkPen(*args, **kargs) self.opts['shadowPen'] = fn.mkPen(*args, **kargs)
@ -283,7 +284,7 @@ class PlotCurveItem(GraphicsObject):
self.opts['brush'] = fn.mkBrush(*args, **kargs) self.opts['brush'] = fn.mkBrush(*args, **kargs)
self.invalidateBounds() self.invalidateBounds()
self.update() self.update()
def setFillLevel(self, level): def setFillLevel(self, level):
"""Set the level filled to when filling under the curve""" """Set the level filled to when filling under the curve"""
self.opts['fillLevel'] = level self.opts['fillLevel'] = level
@ -295,11 +296,11 @@ class PlotCurveItem(GraphicsObject):
""" """
=============== ======================================================== =============== ========================================================
**Arguments:** **Arguments:**
x, y (numpy arrays) Data to show x, y (numpy arrays) Data to show
pen Pen to use when drawing. Any single argument accepted by pen Pen to use when drawing. Any single argument accepted by
:func:`mkPen <pyqtgraph.mkPen>` is allowed. :func:`mkPen <pyqtgraph.mkPen>` is allowed.
shadowPen Pen for drawing behind the primary pen. Usually this shadowPen Pen for drawing behind the primary pen. Usually this
is used to emphasize the curve by providing a is used to emphasize the curve by providing a
high-contrast border. Any single argument accepted by high-contrast border. Any single argument accepted by
:func:`mkPen <pyqtgraph.mkPen>` is allowed. :func:`mkPen <pyqtgraph.mkPen>` is allowed.
fillLevel (float or None) Fill the area 'under' the curve to fillLevel (float or None) Fill the area 'under' the curve to
@ -317,18 +318,18 @@ class PlotCurveItem(GraphicsObject):
to be drawn. "finite" causes segments to be omitted if to be drawn. "finite" causes segments to be omitted if
they are attached to nan or inf values. For any other they are attached to nan or inf values. For any other
connectivity, specify an array of boolean values. connectivity, specify an array of boolean values.
compositionMode See :func:`setCompositionMode compositionMode See :func:`setCompositionMode
<pyqtgraph.PlotCurveItem.setCompositionMode>`. <pyqtgraph.PlotCurveItem.setCompositionMode>`.
=============== ======================================================== =============== ========================================================
If non-keyword arguments are used, they will be interpreted as If non-keyword arguments are used, they will be interpreted as
setData(y) for a single argument and setData(x, y) for two setData(y) for a single argument and setData(x, y) for two
arguments. arguments.
""" """
self.updateData(*args, **kargs) self.updateData(*args, **kargs)
def updateData(self, *args, **kargs): def updateData(self, *args, **kargs):
profiler = debug.Profiler() profiler = debug.Profiler()
@ -340,12 +341,12 @@ class PlotCurveItem(GraphicsObject):
elif len(args) == 2: elif len(args) == 2:
kargs['x'] = args[0] kargs['x'] = args[0]
kargs['y'] = args[1] kargs['y'] = args[1]
if 'y' not in kargs or kargs['y'] is None: if 'y' not in kargs or kargs['y'] is None:
kargs['y'] = np.array([]) kargs['y'] = np.array([])
if 'x' not in kargs or kargs['x'] is None: if 'x' not in kargs or kargs['x'] is None:
kargs['x'] = np.arange(len(kargs['y'])) kargs['x'] = np.arange(len(kargs['y']))
for k in ['x', 'y']: for k in ['x', 'y']:
data = kargs[k] data = kargs[k]
if isinstance(data, list): if isinstance(data, list):
@ -355,9 +356,9 @@ class PlotCurveItem(GraphicsObject):
raise Exception("Plot data must be 1D ndarray.") raise Exception("Plot data must be 1D ndarray.")
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.")
profiler("data checks") profiler("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.invalidateBounds() self.invalidateBounds()
@ -365,24 +366,24 @@ class PlotCurveItem(GraphicsObject):
self.informViewBoundsChanged() self.informViewBoundsChanged()
self.yData = kargs['y'].view(np.ndarray) self.yData = kargs['y'].view(np.ndarray)
self.xData = kargs['x'].view(np.ndarray) self.xData = kargs['x'].view(np.ndarray)
profiler('copy') profiler('copy')
if 'stepMode' in kargs: if 'stepMode' in kargs:
self.opts['stepMode'] = kargs['stepMode'] self.opts['stepMode'] = kargs['stepMode']
if self.opts['stepMode'] is True: if self.opts['stepMode'] is True:
if len(self.xData) != len(self.yData)+1: ## allow difference of 1 for step mode plots if len(self.xData) != len(self.yData)+1: ## allow difference of 1 for step mode plots
raise Exception("len(X) must be len(Y)+1 since stepMode=True (got %s and %s)" % (self.xData.shape, self.yData.shape)) raise Exception("len(X) must be len(Y)+1 since stepMode=True (got %s and %s)" % (self.xData.shape, self.yData.shape))
else: else:
if self.xData.shape != self.yData.shape: ## allow difference of 1 for step mode plots if self.xData.shape != self.yData.shape: ## allow difference of 1 for step mode plots
raise Exception("X and Y arrays must be the same shape--got %s and %s." % (self.xData.shape, self.yData.shape)) raise Exception("X and Y arrays must be the same shape--got %s and %s." % (self.xData.shape, self.yData.shape))
self.path = None self.path = None
self.fillPath = None self.fillPath = None
self._mouseShape = None self._mouseShape = None
#self.xDisp = self.yDisp = None #self.xDisp = self.yDisp = None
if 'name' in kargs: if 'name' in kargs:
self.opts['name'] = kargs['name'] self.opts['name'] = kargs['name']
if 'connect' in kargs: if 'connect' in kargs:
@ -397,14 +398,14 @@ class PlotCurveItem(GraphicsObject):
self.setBrush(kargs['brush']) self.setBrush(kargs['brush'])
if 'antialias' in kargs: if 'antialias' in kargs:
self.opts['antialias'] = kargs['antialias'] self.opts['antialias'] = kargs['antialias']
profiler('set') profiler('set')
self.update() self.update()
profiler('update') profiler('update')
self.sigPlotChanged.emit(self) self.sigPlotChanged.emit(self)
profiler('emit') profiler('emit')
def generatePath(self, x, y): def generatePath(self, x, y):
if self.opts['stepMode']: if self.opts['stepMode']:
## each value in the x/y arrays generates 2 points. ## each value in the x/y arrays generates 2 points.
@ -423,9 +424,9 @@ class PlotCurveItem(GraphicsObject):
y = y2.reshape(y2.size)[1:-1] y = y2.reshape(y2.size)[1:-1]
y[0] = self.opts['fillLevel'] y[0] = self.opts['fillLevel']
y[-1] = self.opts['fillLevel'] y[-1] = self.opts['fillLevel']
path = fn.arrayToQPath(x, y, connect=self.opts['connect']) path = fn.arrayToQPath(x, y, connect=self.opts['connect'])
return path return path
@ -438,7 +439,7 @@ class PlotCurveItem(GraphicsObject):
self.path = self.generatePath(*self.getData()) self.path = self.generatePath(*self.getData())
self.fillPath = None self.fillPath = None
self._mouseShape = None self._mouseShape = None
return self.path return self.path
@debug.warnOnException ## raising an exception here causes crash @debug.warnOnException ## raising an exception here causes crash
@ -446,27 +447,27 @@ class PlotCurveItem(GraphicsObject):
profiler = debug.Profiler() profiler = debug.Profiler()
if self.xData is None or len(self.xData) == 0: if self.xData is None or len(self.xData) == 0:
return return
if HAVE_OPENGL and getConfigOption('enableExperimental') and isinstance(widget, QtOpenGL.QGLWidget): if HAVE_OPENGL and getConfigOption('enableExperimental') and isinstance(widget, QtOpenGL.QGLWidget):
self.paintGL(p, opt, widget) self.paintGL(p, opt, widget)
return return
x = None x = None
y = None y = None
path = self.getPath() path = self.getPath()
profiler('generate path') profiler('generate path')
if self._exportOpts is not False: if self._exportOpts is not False:
aa = self._exportOpts.get('antialias', True) aa = self._exportOpts.get('antialias', True)
else: else:
aa = self.opts['antialias'] aa = self.opts['antialias']
p.setRenderHint(p.Antialiasing, aa) p.setRenderHint(p.Antialiasing, aa)
cmode = self.opts['compositionMode'] cmode = self.opts['compositionMode']
if cmode is not None: if cmode is not None:
p.setCompositionMode(cmode) p.setCompositionMode(cmode)
if self.opts['brush'] is not None and self.opts['fillLevel'] 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:
@ -477,14 +478,14 @@ class PlotCurveItem(GraphicsObject):
p2.lineTo(x[0], y[0]) p2.lineTo(x[0], y[0])
p2.closeSubpath() p2.closeSubpath()
self.fillPath = p2 self.fillPath = p2
profiler('generate fill path') profiler('generate fill path')
p.fillPath(self.fillPath, self.opts['brush']) p.fillPath(self.fillPath, self.opts['brush'])
profiler('draw fill path') profiler('draw fill path')
sp = fn.mkPen(self.opts['shadowPen']) sp = self.opts['shadowPen']
cp = fn.mkPen(self.opts['pen']) cp = self.opts['pen']
## Copy pens and apply alpha adjustment ## Copy pens and apply alpha adjustment
#sp = QtGui.QPen(self.opts['shadowPen']) #sp = QtGui.QPen(self.opts['shadowPen'])
#cp = QtGui.QPen(self.opts['pen']) #cp = QtGui.QPen(self.opts['pen'])
@ -495,9 +496,7 @@ class PlotCurveItem(GraphicsObject):
#c.setAlpha(c.alpha() * self.opts['alphaHint']) #c.setAlpha(c.alpha() * self.opts['alphaHint'])
#pen.setColor(c) #pen.setColor(c)
##pen.setCosmetic(True) ##pen.setCosmetic(True)
if sp is not None and sp.style() != QtCore.Qt.NoPen: if sp is not None and sp.style() != QtCore.Qt.NoPen:
p.setPen(sp) p.setPen(sp)
p.drawPath(path) p.drawPath(path)
@ -507,29 +506,29 @@ class PlotCurveItem(GraphicsObject):
else: else:
p.drawPath(path) p.drawPath(path)
profiler('drawPath') profiler('drawPath')
#print "Render hints:", int(p.renderHints()) #print "Render hints:", int(p.renderHints())
#p.setPen(QtGui.QPen(QtGui.QColor(255,0,0))) #p.setPen(QtGui.QPen(QtGui.QColor(255,0,0)))
#p.drawRect(self.boundingRect()) #p.drawRect(self.boundingRect())
def paintGL(self, p, opt, widget): def paintGL(self, p, opt, widget):
p.beginNativePainting() p.beginNativePainting()
import OpenGL.GL as gl import OpenGL.GL as gl
## set clipping viewport ## set clipping viewport
view = self.getViewBox() view = self.getViewBox()
if view is not None: if view is not None:
rect = view.mapRectToItem(self, view.boundingRect()) rect = view.mapRectToItem(self, view.boundingRect())
#gl.glViewport(int(rect.x()), int(rect.y()), int(rect.width()), int(rect.height())) #gl.glViewport(int(rect.x()), int(rect.y()), int(rect.width()), int(rect.height()))
#gl.glTranslate(-rect.x(), -rect.y(), 0) #gl.glTranslate(-rect.x(), -rect.y(), 0)
gl.glEnable(gl.GL_STENCIL_TEST) gl.glEnable(gl.GL_STENCIL_TEST)
gl.glColorMask(gl.GL_FALSE, gl.GL_FALSE, gl.GL_FALSE, gl.GL_FALSE) # disable drawing to frame buffer gl.glColorMask(gl.GL_FALSE, gl.GL_FALSE, gl.GL_FALSE, gl.GL_FALSE) # disable drawing to frame buffer
gl.glDepthMask(gl.GL_FALSE) # disable drawing to depth buffer gl.glDepthMask(gl.GL_FALSE) # disable drawing to depth buffer
gl.glStencilFunc(gl.GL_NEVER, 1, 0xFF) gl.glStencilFunc(gl.GL_NEVER, 1, 0xFF)
gl.glStencilOp(gl.GL_REPLACE, gl.GL_KEEP, gl.GL_KEEP) gl.glStencilOp(gl.GL_REPLACE, gl.GL_KEEP, gl.GL_KEEP)
## draw stencil pattern ## draw stencil pattern
gl.glStencilMask(0xFF) gl.glStencilMask(0xFF)
gl.glClear(gl.GL_STENCIL_BUFFER_BIT) gl.glClear(gl.GL_STENCIL_BUFFER_BIT)
@ -541,12 +540,12 @@ class PlotCurveItem(GraphicsObject):
gl.glVertex2f(rect.x()+rect.width(), rect.y()) gl.glVertex2f(rect.x()+rect.width(), rect.y())
gl.glVertex2f(rect.x(), rect.y()+rect.height()) gl.glVertex2f(rect.x(), rect.y()+rect.height())
gl.glEnd() gl.glEnd()
gl.glColorMask(gl.GL_TRUE, gl.GL_TRUE, gl.GL_TRUE, gl.GL_TRUE) gl.glColorMask(gl.GL_TRUE, gl.GL_TRUE, gl.GL_TRUE, gl.GL_TRUE)
gl.glDepthMask(gl.GL_TRUE) gl.glDepthMask(gl.GL_TRUE)
gl.glStencilMask(0x00) gl.glStencilMask(0x00)
gl.glStencilFunc(gl.GL_EQUAL, 1, 0xFF) gl.glStencilFunc(gl.GL_EQUAL, 1, 0xFF)
try: try:
x, y = self.getData() x, y = self.getData()
pos = np.empty((len(x), 2)) pos = np.empty((len(x), 2))
@ -571,7 +570,7 @@ class PlotCurveItem(GraphicsObject):
gl.glDisableClientState(gl.GL_VERTEX_ARRAY) gl.glDisableClientState(gl.GL_VERTEX_ARRAY)
finally: finally:
p.endNativePainting() p.endNativePainting()
def clear(self): def clear(self):
self.xData = None ## raw values self.xData = None ## raw values
self.yData = None self.yData = None
@ -587,7 +586,7 @@ class PlotCurveItem(GraphicsObject):
def mouseShape(self): def mouseShape(self):
""" """
Return a QPainterPath representing the clickable shape of the curve Return a QPainterPath representing the clickable shape of the curve
""" """
if self._mouseShape is None: if self._mouseShape is None:
view = self.getViewBox() view = self.getViewBox()
@ -600,14 +599,14 @@ class PlotCurveItem(GraphicsObject):
mousePath = stroker.createStroke(path) mousePath = stroker.createStroke(path)
self._mouseShape = self.mapFromItem(view, mousePath) self._mouseShape = self.mapFromItem(view, mousePath)
return self._mouseShape return self._mouseShape
def mouseClickEvent(self, ev): def mouseClickEvent(self, ev):
if not self.clickable or ev.button() != QtCore.Qt.LeftButton: if not self.clickable or ev.button() != QtCore.Qt.LeftButton:
return return
if self.mouseShape().contains(ev.pos()): if self.mouseShape().contains(ev.pos()):
ev.accept() ev.accept()
self.sigClicked.emit(self) self.sigClicked.emit(self)
class ROIPlotItem(PlotCurveItem): class ROIPlotItem(PlotCurveItem):
@ -622,7 +621,7 @@ class ROIPlotItem(PlotCurveItem):
#roi.connect(roi, QtCore.SIGNAL('regionChanged'), self.roiChangedEvent) #roi.connect(roi, QtCore.SIGNAL('regionChanged'), self.roiChangedEvent)
roi.sigRegionChanged.connect(self.roiChangedEvent) roi.sigRegionChanged.connect(self.roiChangedEvent)
#self.roiChangedEvent() #self.roiChangedEvent()
def getRoiData(self): def getRoiData(self):
d = self.roi.getArrayRegion(self.roiData, self.roiImg, axes=self.axes) d = self.roi.getArrayRegion(self.roiData, self.roiImg, axes=self.axes)
if d is None: if d is None:
@ -630,7 +629,7 @@ class ROIPlotItem(PlotCurveItem):
while d.ndim > 1: while d.ndim > 1:
d = d.mean(axis=1) d = d.mean(axis=1)
return d return d
def roiChangedEvent(self): def roiChangedEvent(self):
d = self.getRoiData() d = self.getRoiData()
self.updateData(d, self.xVals) self.updateData(d, self.xVals)