pyqtgraph/pyqtgraph/graphicsItems/ErrorBarItem.py

161 lines
5.4 KiB
Python

# -*- coding: utf-8 -*-
from ..Qt import QtGui, QtCore
from .GraphicsObject import GraphicsObject
from .. import getConfigOption
from .. import functions as fn
import numpy as np
__all__ = ['ErrorBarItem']
class ErrorBarItem(GraphicsObject):
def __init__(self, **opts):
"""
All keyword arguments are passed to setData().
"""
GraphicsObject.__init__(self)
self.opts = dict(
x=None,
y=None,
height=None,
width=None,
top=None,
bottom=None,
left=None,
right=None,
beam=None,
pen=None
)
self.setVisible(False)
self.setData(**opts)
def setData(self, **opts):
"""
Update the data in the item. All arguments are optional.
Valid keyword options are:
x, y, height, width, top, bottom, left, right, beam, pen
* x and y must be numpy arrays specifying the coordinates of data points.
* height, width, top, bottom, left, right, and beam may be numpy arrays,
single values, or None to disable. All values should be positive.
* top, bottom, left, and right specify the lengths of bars extending
in each direction.
* If height is specified, it overrides top and bottom.
* If width is specified, it overrides left and right.
* beam specifies the width of the beam at the end of each bar.
* pen may be any single argument accepted by pg.mkPen().
This method was added in version 0.9.9. For prior versions, use setOpts.
"""
self.opts.update(opts)
self.setVisible(all(self.opts[ax] is not None for ax in ['x', 'y']))
self.path = None
self.update()
self.prepareGeometryChange()
self.informViewBoundsChanged()
def setOpts(self, **opts):
# for backward compatibility
self.setData(**opts)
def drawPath(self):
p = QtGui.QPainterPath()
x, y = self.opts['x'], self.opts['y']
if x is None or y is None:
self.path = p
return
beam = self.opts['beam']
height, top, bottom = self.opts['height'], self.opts['top'], self.opts['bottom']
if height is not None or top is not None or bottom is not None:
## draw vertical error bars
if height is not None:
y1 = y - height/2.
y2 = y + height/2.
else:
if bottom is None:
y1 = y
else:
y1 = y - bottom
if top is None:
y2 = y
else:
y2 = y + top
xs = fn.interweaveArrays(x, x)
y1_y2 = fn.interweaveArrays(y1, y2)
verticalLines = fn.arrayToQPath(xs, y1_y2, connect="pairs")
p.addPath(verticalLines)
if beam is not None and beam > 0:
x1 = x - beam/2.
x2 = x + beam/2.
x1_x2 = fn.interweaveArrays(x1, x2)
if height is not None or top is not None:
y2s = fn.interweaveArrays(y2, y2)
topEnds = fn.arrayToQPath(x1_x2, y2s, connect="pairs")
p.addPath(topEnds)
if height is not None or bottom is not None:
y1s = fn.interweaveArrays(y1, y1)
bottomEnds = fn.arrayToQPath(x1_x2, y1s, connect="pairs")
p.addPath(bottomEnds)
width, right, left = self.opts['width'], self.opts['right'], self.opts['left']
if width is not None or right is not None or left is not None:
## draw vertical error bars
if width is not None:
x1 = x - width/2.
x2 = x + width/2.
else:
if left is None:
x1 = x
else:
x1 = x - left
if right is None:
x2 = x
else:
x2 = x + right
ys = fn.interweaveArrays(y, y)
x1_x2 = fn.interweaveArrays(x1, x2)
ends = fn.arrayToQPath(x1_x2, ys, connect='pairs')
p.addPath(ends)
if beam is not None and beam > 0:
y1 = y - beam/2.
y2 = y + beam/2.
y1_y2 = fn.interweaveArrays(y1, y2)
if width is not None or right is not None:
x2s = fn.interweaveArrays(x2, x2)
rightEnds = fn.arrayToQPath(x2s, y1_y2, connect="pairs")
p.addPath(rightEnds)
if width is not None or left is not None:
x1s = fn.interweaveArrays(x1, x1)
leftEnds = fn.arrayToQPath(x1s, y1_y2, connect="pairs")
p.addPath(leftEnds)
self.path = p
self.prepareGeometryChange()
def paint(self, p, *args):
if self.path is None:
self.drawPath()
pen = self.opts['pen']
if pen is None:
pen = getConfigOption('foreground')
p.setPen(fn.mkPen(pen))
p.drawPath(self.path)
def boundingRect(self):
if self.path is None:
self.drawPath()
return self.path.boundingRect()