2013-12-22 07:08:39 +00:00
from . . Qt import QtGui , QtCore
2013-03-05 00:43:51 +00:00
try :
2013-12-22 07:08:39 +00:00
from . . Qt import QtOpenGL
2013-03-05 00:43:51 +00:00
HAVE_OPENGL = True
except :
HAVE_OPENGL = False
2012-03-02 02:55:32 +00:00
import numpy as np
2012-05-11 22:05:41 +00:00
from . GraphicsObject import GraphicsObject
2013-12-22 07:08:39 +00:00
from . . import functions as fn
from . . Point import Point
2012-05-11 22:05:41 +00:00
import struct , sys
2013-12-22 07:08:39 +00:00
from . . import getConfigOption
from . . import debug
2012-03-02 02:55:32 +00:00
__all__ = [ ' PlotCurveItem ' ]
class PlotCurveItem ( GraphicsObject ) :
2012-04-21 19:55:27 +00:00
"""
Class representing a single plot curve . Instances of this class are created
automatically as part of PlotDataItem ; these rarely need to be instantiated
directly .
Features :
- Fast data update
- Fill under curve
- Mouse interaction
== == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == =
* * Signals : * *
sigPlotChanged ( self ) Emitted when the data being plotted has changed
sigClicked ( self ) Emitted when the curve is clicked
== == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == =
2012-03-02 02:55:32 +00:00
"""
sigPlotChanged = QtCore . Signal ( object )
sigClicked = QtCore . Signal ( object )
2012-04-23 18:57:49 +00:00
def __init__ ( self , * args , * * kargs ) :
2012-04-21 19:55:27 +00:00
"""
2012-04-23 18:57:49 +00:00
Forwards all arguments to : func : ` setData < pyqtgraph . PlotCurveItem . setData > ` .
Some extra arguments are accepted as well :
2012-04-21 19:55:27 +00:00
== == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == =
* * Arguments : * *
2012-04-23 18:57:49 +00:00
parent The parent GraphicsObject ( optional )
2012-04-21 19:55:27 +00:00
clickable If True , the item will emit sigClicked when it is
2012-04-23 18:57:49 +00:00
clicked on . Defaults to False .
2012-04-21 19:55:27 +00:00
== == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == =
"""
2012-04-23 18:57:49 +00:00
GraphicsObject . __init__ ( self , kargs . get ( ' parent ' , None ) )
2012-03-02 02:55:32 +00:00
self . clear ( )
## this is disastrous for performance.
#self.setCacheMode(QtGui.QGraphicsItem.DeviceCoordinateCache)
self . metaData = { }
self . opts = {
2012-04-23 18:57:49 +00:00
' pen ' : fn . mkPen ( ' w ' ) ,
2012-03-18 18:57:36 +00:00
' shadowPen ' : None ,
2012-04-23 18:57:49 +00:00
' fillLevel ' : None ,
' brush ' : None ,
2012-10-22 17:35:58 +00:00
' stepMode ' : False ,
2012-12-23 05:51:28 +00:00
' name ' : None ,
2013-12-22 07:08:39 +00:00
' antialias ' : getConfigOption ( ' antialias ' ) ,
2013-09-13 07:27:26 +00:00
' connect ' : ' all ' ,
2013-12-19 14:56:58 +00:00
' mouseWidth ' : 8 , # width of shape responding to mouse click
2012-03-02 02:55:32 +00:00
}
2012-04-23 18:57:49 +00:00
self . setClickable ( kargs . get ( ' clickable ' , False ) )
self . setData ( * args , * * kargs )
2012-03-02 02:55:32 +00:00
def implements ( self , interface = None ) :
ints = [ ' plotData ' ]
if interface is None :
return ints
return interface in ints
2013-12-19 17:30:00 +00:00
def name ( self ) :
return self . opts . get ( ' name ' , None )
2013-12-19 14:56:58 +00:00
def setClickable ( self , s , width = None ) :
""" Sets whether the item responds to mouse clicks.
The * width * argument specifies the width in pixels orthogonal to the
curve that will respond to a mouse click .
"""
2012-03-02 02:55:32 +00:00
self . clickable = s
2013-12-19 14:56:58 +00:00
if width is not None :
self . opts [ ' mouseWidth ' ] = width
self . _mouseShape = None
self . _boundingRect = None
2012-03-02 02:55:32 +00:00
def getData ( self ) :
2012-03-18 18:57:36 +00:00
return self . xData , self . yData
2012-03-02 02:55:32 +00:00
2012-05-08 21:56:55 +00:00
def dataBounds ( self , ax , frac = 1.0 , orthoRange = None ) :
2013-01-10 03:21:32 +00:00
## Need this to run as fast as possible.
## check cache first:
cache = self . _boundsCache [ ax ]
if cache is not None and cache [ 0 ] == ( frac , orthoRange ) :
return cache [ 1 ]
2012-03-02 02:55:32 +00:00
( x , y ) = self . getData ( )
if x is None or len ( x ) == 0 :
2013-02-13 02:44:42 +00:00
return ( None , None )
2012-03-02 02:55:32 +00:00
if ax == 0 :
d = x
2012-05-08 21:56:55 +00:00
d2 = y
2012-03-02 02:55:32 +00:00
elif ax == 1 :
d = y
2012-05-08 21:56:55 +00:00
d2 = x
2013-02-13 02:44:42 +00:00
## If an orthogonal range is specified, mask the data now
2012-05-08 21:56:55 +00:00
if orthoRange is not None :
mask = ( d2 > = orthoRange [ 0 ] ) * ( d2 < = orthoRange [ 1 ] )
d = d [ mask ]
2013-03-17 18:26:23 +00:00
#d2 = d2[mask]
if len ( d ) == 0 :
return ( None , None )
2012-05-08 21:56:55 +00:00
2013-02-13 02:44:42 +00:00
## Get min/max (or percentiles) of the requested data range
2012-03-02 02:55:32 +00:00
if frac > = 1.0 :
2013-09-13 07:27:26 +00:00
b = ( np . nanmin ( d ) , np . nanmax ( d ) )
2012-03-02 02:55:32 +00:00
elif frac < = 0.0 :
raise Exception ( " Value for parameter ' frac ' must be > 0. (got %s ) " % str ( frac ) )
else :
2013-09-13 07:27:26 +00:00
mask = np . isfinite ( d )
d = d [ mask ]
2013-11-15 19:00:12 +00:00
b = np . percentile ( d , [ 50 * ( 1 - frac ) , 50 * ( 1 + frac ) ] )
2013-02-13 02:44:42 +00:00
## 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 )
2013-01-10 03:21:32 +00:00
self . _boundsCache [ ax ] = [ ( frac , orthoRange ) , b ]
return b
2013-02-13 02:44:42 +00:00
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 )
2013-12-19 14:56:58 +00:00
if self . clickable :
w = max ( w , self . opts [ ' mouseWidth ' ] / / 2 + 1 )
2013-02-13 02:44:42 +00:00
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 )
2013-12-19 14:56:58 +00:00
2013-02-13 02:44:42 +00:00
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
2013-01-10 03:21:32 +00:00
def invalidateBounds ( self ) :
self . _boundingRect = None
self . _boundsCache = [ None , None ]
2012-03-02 02:55:32 +00:00
2012-03-18 18:57:36 +00:00
def setPen ( self , * args , * * kargs ) :
2012-04-21 19:55:27 +00:00
""" Set the pen used to draw the curve. """
2012-03-18 18:57:36 +00:00
self . opts [ ' pen ' ] = fn . mkPen ( * args , * * kargs )
2013-01-10 03:21:32 +00:00
self . invalidateBounds ( )
2012-03-02 02:55:32 +00:00
self . update ( )
2012-03-18 18:57:36 +00:00
def setShadowPen ( self , * args , * * kargs ) :
2012-04-21 19:55:27 +00:00
""" Set the shadow pen used to draw behind tyhe primary pen.
This pen must have a larger width than the primary
pen to be visible .
"""
2012-03-18 18:57:36 +00:00
self . opts [ ' shadowPen ' ] = fn . mkPen ( * args , * * kargs )
2013-01-10 03:21:32 +00:00
self . invalidateBounds ( )
2012-03-02 02:55:32 +00:00
self . update ( )
2012-03-18 18:57:36 +00:00
def setBrush ( self , * args , * * kargs ) :
2012-04-21 19:55:27 +00:00
""" Set the brush used when filling the area under the curve """
2012-03-18 18:57:36 +00:00
self . opts [ ' brush ' ] = fn . mkBrush ( * args , * * kargs )
2013-01-10 03:21:32 +00:00
self . invalidateBounds ( )
2012-03-02 02:55:32 +00:00
self . update ( )
2012-03-18 18:57:36 +00:00
def setFillLevel ( self , level ) :
2012-04-21 19:55:27 +00:00
""" Set the level filled to when filling under the curve """
2012-03-18 18:57:36 +00:00
self . opts [ ' fillLevel ' ] = level
self . fillPath = None
2013-01-10 03:21:32 +00:00
self . invalidateBounds ( )
2012-03-02 02:55:32 +00:00
self . update ( )
2012-03-18 18:57:36 +00:00
def setData ( self , * args , * * kargs ) :
2012-04-21 19:55:27 +00:00
"""
2012-12-23 05:51:28 +00:00
== == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == ==
2012-04-23 18:57:49 +00:00
* * Arguments : * *
x , y ( numpy arrays ) Data to show
pen Pen to use when drawing . Any single argument accepted by
: func : ` mkPen < pyqtgraph . mkPen > ` is allowed .
shadowPen Pen for drawing behind the primary pen . Usually this
is used to emphasize the curve by providing a
high - contrast border . Any single argument accepted by
: func : ` mkPen < pyqtgraph . mkPen > ` is allowed .
fillLevel ( float or None ) Fill the area ' under ' the curve to
* fillLevel *
brush QBrush to use when filling . Any single argument accepted
by : func : ` mkBrush < pyqtgraph . mkBrush > ` is allowed .
2012-12-23 05:51:28 +00:00
antialias ( bool ) Whether to use antialiasing when drawing . This
is disabled by default because it decreases performance .
2013-08-04 18:36:14 +00:00
stepMode If True , two orthogonal lines are drawn for each sample
as steps . This is commonly used when drawing histograms .
Note that in this case , len ( x ) == len ( y ) + 1
2013-09-13 07:27:26 +00:00
connect Argument specifying how vertexes should be connected
by line segments . Default is " all " , indicating full
connection . " pairs " causes only even - numbered segments
to be drawn . " finite " causes segments to be omitted if
they are attached to nan or inf values . For any other
connectivity , specify an array of boolean values .
2012-12-23 05:51:28 +00:00
== == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == ==
2012-04-23 18:57:49 +00:00
If non - keyword arguments are used , they will be interpreted as
setData ( y ) for a single argument and setData ( x , y ) for two
arguments .
2012-04-21 19:55:27 +00:00
"""
2012-03-18 18:57:36 +00:00
self . updateData ( * args , * * kargs )
2012-03-02 02:55:32 +00:00
2012-03-18 18:57:36 +00:00
def updateData ( self , * args , * * kargs ) :
2013-11-27 06:16:13 +00:00
profiler = debug . Profiler ( )
2012-03-18 18:57:36 +00:00
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 ) :
2012-03-18 23:48:40 +00:00
data = np . array ( data )
kargs [ k ] = data
2012-03-18 18:57:36 +00:00
if not isinstance ( data , np . ndarray ) or data . ndim > 1 :
raise Exception ( " Plot data must be 1D ndarray. " )
2012-03-02 02:55:32 +00:00
if ' complex ' in str ( data . dtype ) :
raise Exception ( " Can not plot complex data types. " )
2012-03-18 18:57:36 +00:00
2013-11-27 06:16:13 +00:00
profiler ( " data checks " )
2012-03-02 02:55:32 +00:00
2012-03-18 18:57:36 +00:00
#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
2012-03-02 02:55:32 +00:00
## Test this bug with test_PlotWidget and zoom in on the animated plot
2013-01-10 03:21:32 +00:00
self . invalidateBounds ( )
2012-03-02 02:55:32 +00:00
self . prepareGeometryChange ( )
2013-01-10 03:21:32 +00:00
self . informViewBoundsChanged ( )
2012-03-18 18:57:36 +00:00
self . yData = kargs [ ' y ' ] . view ( np . ndarray )
self . xData = kargs [ ' x ' ] . view ( np . ndarray )
2013-11-27 06:16:13 +00:00
profiler ( ' copy ' )
2012-03-02 02:55:32 +00:00
2012-10-22 17:35:58 +00:00
if ' stepMode ' in kargs :
self . opts [ ' stepMode ' ] = kargs [ ' stepMode ' ]
if self . opts [ ' stepMode ' ] is True :
if len ( self . xData ) != len ( self . yData ) + 1 : ## allow difference of 1 for step mode plots
2013-06-20 12:44:46 +00:00
raise Exception ( " len(X) must be len(Y)+1 since stepMode=True (got %s and %s ) " % ( self . xData . shape , self . yData . shape ) )
2012-10-22 17:35:58 +00:00
else :
if self . xData . shape != self . yData . shape : ## allow difference of 1 for step mode plots
2013-06-20 12:44:46 +00:00
raise Exception ( " X and Y arrays must be the same shape--got %s and %s . " % ( self . xData . shape , self . yData . shape ) )
2012-03-02 02:55:32 +00:00
self . path = None
2012-03-18 18:57:36 +00:00
self . fillPath = None
2013-12-19 14:56:58 +00:00
self . _mouseShape = None
2012-03-18 18:57:36 +00:00
#self.xDisp = self.yDisp = None
2012-11-23 21:05:14 +00:00
if ' name ' in kargs :
self . opts [ ' name ' ] = kargs [ ' name ' ]
2013-09-13 07:27:26 +00:00
if ' connect ' in kargs :
self . opts [ ' connect ' ] = kargs [ ' connect ' ]
2012-03-18 18:57:36 +00:00
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 ' ] )
2012-12-23 05:51:28 +00:00
if ' antialias ' in kargs :
self . opts [ ' antialias ' ] = kargs [ ' antialias ' ]
2012-03-18 18:57:36 +00:00
2012-03-02 02:55:32 +00:00
2013-11-27 06:16:13 +00:00
profiler ( ' set ' )
2012-03-02 02:55:32 +00:00
self . update ( )
2013-11-27 06:16:13 +00:00
profiler ( ' update ' )
2012-03-02 02:55:32 +00:00
self . sigPlotChanged . emit ( self )
2013-11-27 06:16:13 +00:00
profiler ( ' emit ' )
2012-03-02 02:55:32 +00:00
def generatePath ( self , x , y ) :
2012-10-22 17:35:58 +00:00
if self . opts [ ' stepMode ' ] :
## each value in the x/y arrays generates 2 points.
x2 = np . empty ( ( len ( x ) , 2 ) , dtype = x . dtype )
x2 [ : ] = x [ : , np . newaxis ]
if self . opts [ ' fillLevel ' ] is None :
x = x2 . reshape ( x2 . size ) [ 1 : - 1 ]
y2 = np . empty ( ( len ( y ) , 2 ) , dtype = y . dtype )
y2 [ : ] = y [ : , np . newaxis ]
y = y2 . reshape ( y2 . size )
else :
## If we have a fill level, add two extra points at either end
x = x2 . reshape ( x2 . size )
y2 = np . empty ( ( len ( y ) + 2 , 2 ) , dtype = y . dtype )
y2 [ 1 : - 1 ] = y [ : , np . newaxis ]
y = y2 . reshape ( y2 . size ) [ 1 : - 1 ]
y [ 0 ] = self . opts [ ' fillLevel ' ]
y [ - 1 ] = self . opts [ ' fillLevel ' ]
2013-09-13 07:27:26 +00:00
path = fn . arrayToQPath ( x , y , connect = self . opts [ ' connect ' ] )
2012-03-02 02:55:32 +00:00
return path
2013-12-19 14:56:58 +00:00
def getPath ( self ) :
2012-03-02 02:55:32 +00:00
if self . path is None :
2013-12-19 14:56:58 +00:00
x , y = self . getData ( )
if x is None or len ( x ) == 0 or y is None or len ( y ) == 0 :
2012-03-02 02:55:32 +00:00
return QtGui . QPainterPath ( )
2013-12-19 14:56:58 +00:00
self . path = self . generatePath ( * self . getData ( ) )
self . fillPath = None
self . _mouseShape = None
2012-03-02 02:55:32 +00:00
return self . path
2013-12-22 07:08:39 +00:00
@debug.warnOnException ## raising an exception here causes crash
2012-03-02 02:55:32 +00:00
def paint ( self , p , opt , widget ) :
2013-11-27 06:16:13 +00:00
profiler = debug . Profiler ( )
2012-03-02 02:55:32 +00:00
if self . xData is None :
return
2013-03-05 00:43:51 +00:00
2013-12-22 07:08:39 +00:00
if HAVE_OPENGL and getConfigOption ( ' enableExperimental ' ) and isinstance ( widget , QtOpenGL . QGLWidget ) :
2013-03-05 00:43:51 +00:00
self . paintGL ( p , opt , widget )
return
2012-03-02 02:55:32 +00:00
x = None
y = None
2013-12-19 14:56:58 +00:00
path = self . getPath ( )
2013-11-27 06:16:13 +00:00
profiler ( ' generate path ' )
2012-12-23 05:51:28 +00:00
2012-12-25 05:43:31 +00:00
if self . _exportOpts is not False :
aa = self . _exportOpts . get ( ' antialias ' , True )
2012-12-23 05:51:28 +00:00
else :
aa = self . opts [ ' antialias ' ]
p . setRenderHint ( p . Antialiasing , aa )
2012-03-02 02:55:32 +00:00
2012-03-18 18:57:36 +00:00
if self . opts [ ' brush ' ] is not None and self . opts [ ' fillLevel ' ] is not None :
2012-03-02 02:55:32 +00:00
if self . fillPath is None :
if x is None :
x , y = self . getData ( )
p2 = QtGui . QPainterPath ( self . path )
2012-03-18 18:57:36 +00:00
p2 . lineTo ( x [ - 1 ] , self . opts [ ' fillLevel ' ] )
p2 . lineTo ( x [ 0 ] , self . opts [ ' fillLevel ' ] )
p2 . lineTo ( x [ 0 ] , y [ 0 ] )
2012-03-02 02:55:32 +00:00
p2 . closeSubpath ( )
self . fillPath = p2
2013-11-27 06:16:13 +00:00
profiler ( ' generate fill path ' )
2012-03-18 18:57:36 +00:00
p . fillPath ( self . fillPath , self . opts [ ' brush ' ] )
2013-11-27 06:16:13 +00:00
profiler ( ' draw fill path ' )
2012-03-02 02:55:32 +00:00
2013-01-07 15:45:17 +00:00
sp = fn . mkPen ( self . opts [ ' shadowPen ' ] )
cp = fn . mkPen ( self . opts [ ' pen ' ] )
2012-03-02 02:55:32 +00:00
## Copy pens and apply alpha adjustment
2013-01-07 15:45:17 +00:00
#sp = QtGui.QPen(self.opts['shadowPen'])
#cp = QtGui.QPen(self.opts['pen'])
2012-03-18 18:57:36 +00:00
#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)
2012-03-02 02:55:32 +00:00
2012-03-23 06:41:10 +00:00
2012-12-25 05:43:31 +00:00
if sp is not None and sp . style ( ) != QtCore . Qt . NoPen :
2012-03-02 02:55:32 +00:00
p . setPen ( sp )
p . drawPath ( path )
p . setPen ( cp )
p . drawPath ( path )
2013-11-27 06:16:13 +00:00
profiler ( ' drawPath ' )
2012-03-02 02:55:32 +00:00
#print "Render hints:", int(p.renderHints())
#p.setPen(QtGui.QPen(QtGui.QColor(255,0,0)))
#p.drawRect(self.boundingRect())
2013-03-05 00:43:51 +00:00
def paintGL ( self , p , opt , widget ) :
p . beginNativePainting ( )
import OpenGL . GL as gl
## set clipping viewport
view = self . getViewBox ( )
if view is not None :
rect = view . mapRectToItem ( self , view . boundingRect ( ) )
#gl.glViewport(int(rect.x()), int(rect.y()), int(rect.width()), int(rect.height()))
#gl.glTranslate(-rect.x(), -rect.y(), 0)
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 . glDepthMask ( gl . GL_FALSE ) # disable drawing to depth buffer
gl . glStencilFunc ( gl . GL_NEVER , 1 , 0xFF )
gl . glStencilOp ( gl . GL_REPLACE , gl . GL_KEEP , gl . GL_KEEP )
## draw stencil pattern
gl . glStencilMask ( 0xFF ) ;
gl . glClear ( gl . GL_STENCIL_BUFFER_BIT )
gl . glBegin ( gl . GL_TRIANGLES )
gl . glVertex2f ( rect . x ( ) , rect . y ( ) )
gl . glVertex2f ( rect . x ( ) + rect . width ( ) , rect . y ( ) )
gl . glVertex2f ( rect . x ( ) , rect . y ( ) + rect . height ( ) )
gl . glVertex2f ( rect . x ( ) + rect . width ( ) , rect . y ( ) + rect . height ( ) )
gl . glVertex2f ( rect . x ( ) + rect . width ( ) , rect . y ( ) )
gl . glVertex2f ( rect . x ( ) , rect . y ( ) + rect . height ( ) )
gl . glEnd ( )
gl . glColorMask ( gl . GL_TRUE , gl . GL_TRUE , gl . GL_TRUE , gl . GL_TRUE )
gl . glDepthMask ( gl . GL_TRUE )
gl . glStencilMask ( 0x00 )
gl . glStencilFunc ( gl . GL_EQUAL , 1 , 0xFF )
try :
x , y = self . getData ( )
pos = np . empty ( ( len ( x ) , 2 ) )
pos [ : , 0 ] = x
pos [ : , 1 ] = y
gl . glEnableClientState ( gl . GL_VERTEX_ARRAY )
try :
gl . glVertexPointerf ( pos )
pen = fn . mkPen ( self . opts [ ' pen ' ] )
color = pen . color ( )
gl . glColor4f ( color . red ( ) / 255. , color . green ( ) / 255. , color . blue ( ) / 255. , color . alpha ( ) / 255. )
width = pen . width ( )
if pen . isCosmetic ( ) and width < 1 :
width = 1
gl . glPointSize ( width )
gl . glEnable ( gl . GL_LINE_SMOOTH )
gl . glEnable ( gl . GL_BLEND )
gl . glBlendFunc ( gl . GL_SRC_ALPHA , gl . GL_ONE_MINUS_SRC_ALPHA )
gl . glHint ( gl . GL_LINE_SMOOTH_HINT , gl . GL_NICEST ) ;
gl . glDrawArrays ( gl . GL_LINE_STRIP , 0 , pos . size / pos . shape [ - 1 ] )
finally :
gl . glDisableClientState ( gl . GL_VERTEX_ARRAY )
finally :
p . endNativePainting ( )
2012-03-02 02:55:32 +00:00
def clear ( self ) :
self . xData = None ## raw values
self . yData = None
self . xDisp = None ## display values (after log / fft)
self . yDisp = None
self . path = None
2013-12-19 14:56:58 +00:00
self . fillPath = None
self . _mouseShape = None
self . _mouseBounds = None
self . _boundsCache = [ None , None ]
2012-03-02 02:55:32 +00:00
#del self.xData, self.yData, self.xDisp, self.yDisp, self.path
2013-12-19 14:56:58 +00:00
def mouseShape ( self ) :
"""
Return a QPainterPath representing the clickable shape of the curve
"""
if self . _mouseShape is None :
view = self . getViewBox ( )
if view is None :
return QtGui . QPainterPath ( )
stroker = QtGui . QPainterPathStroker ( )
path = self . getPath ( )
path = self . mapToItem ( view , path )
stroker . setWidth ( self . opts [ ' mouseWidth ' ] )
mousePath = stroker . createStroke ( path )
self . _mouseShape = self . mapFromItem ( view , mousePath )
return self . _mouseShape
2012-03-02 02:55:32 +00:00
def mouseClickEvent ( self , ev ) :
if not self . clickable or ev . button ( ) != QtCore . Qt . LeftButton :
return
2013-12-19 14:56:58 +00:00
if self . mouseShape ( ) . contains ( ev . pos ( ) ) :
ev . accept ( )
self . sigClicked . emit ( self )
2012-03-02 02:55:32 +00:00
class ROIPlotItem ( PlotCurveItem ) :
""" Plot curve that monitors an ROI and image for changes to automatically replot. """
def __init__ ( self , roi , data , img , axes = ( 0 , 1 ) , xVals = None , color = None ) :
self . roi = roi
self . roiData = data
self . roiImg = img
self . axes = axes
self . xVals = xVals
PlotCurveItem . __init__ ( self , self . getRoiData ( ) , x = self . xVals , color = color )
#roi.connect(roi, QtCore.SIGNAL('regionChanged'), self.roiChangedEvent)
roi . sigRegionChanged . connect ( self . roiChangedEvent )
#self.roiChangedEvent()
def getRoiData ( self ) :
d = self . roi . getArrayRegion ( self . roiData , self . roiImg , axes = self . axes )
if d is None :
return
while d . ndim > 1 :
d = d . mean ( axis = 1 )
return d
def roiChangedEvent ( self ) :
d = self . getRoiData ( )
self . updateData ( d , self . xVals )