Source code for pyqtgraph.graphicsItems.BarGraphItem

import numpy as np

from .. import functions as fn
from .. import getConfigOption
from .. import Qt
from ..Qt import QtCore, QtGui
from .GraphicsObject import GraphicsObject

__all__ = ['BarGraphItem']

[docs] class BarGraphItem(GraphicsObject):
[docs] def __init__(self, **opts): """ Valid keyword options are: x, x0, x1, y, y0, y1, width, height, pen, brush x specifies the x-position of the center of the bar. x0, x1 specify left and right edges of the bar, respectively. width specifies distance from x0 to x1. You may specify any combination: x, width x0, width x1, width x0, x1 Likewise y, y0, y1, and height. If only height is specified, then y0 will be set to 0 Example uses: BarGraphItem(x=range(5), height=[1,5,2,4,3], width=0.5) """ GraphicsObject.__init__(self) self.opts = dict( x=None, y=None, x0=None, y0=None, x1=None, y1=None, name=None, height=None, width=None, pen=None, brush=None, pens=None, brushes=None, ) if 'pen' not in opts: opts['pen'] = getConfigOption('foreground') if 'brush' not in opts: opts['brush'] = (128, 128, 128) # the first call to _updateColors() will thus always be an update self._rectarray = Qt.internals.PrimitiveArray(QtCore.QRectF, 4) self._shape = None self.picture = None self.setOpts(**opts)
def setOpts(self, **opts): self.opts.update(opts) self.picture = None self._shape = None self._prepareData() self._updateColors(opts) self.prepareGeometryChange() self.update() self.informViewBoundsChanged() def _updatePenWidth(self, pen): no_pen = pen is None or pen.style() == QtCore.Qt.PenStyle.NoPen if no_pen: return idx = pen.isCosmetic() self._penWidth[idx] = max(self._penWidth[idx], pen.widthF()) def _updateColors(self, opts): # the logic here is to permit the user to update only data # without updating pens/brushes # update only if fresh pen/pens supplied if 'pen' in opts or 'pens' in opts: self._penWidth = [0, 0] if self.opts['pens'] is None: # pens not configured, use single pen pen = self.opts['pen'] pen = fn.mkPen(pen) self._updatePenWidth(pen) self._sharedPen = pen self._pens = None else: # pens configured, ignore single pen (if any) pens = [] for pen in self.opts['pens']: if not isinstance(pen, QtGui.QPen): pen = fn.mkPen(pen) pens.append(pen) self._updatePenWidth(pen) self._sharedPen = None self._pens = pens # update only if fresh brush/brushes supplied if 'brush' in opts or 'brushes' in opts: if self.opts['brushes'] is None: # brushes not configured, use single brush brush = self.opts['brush'] self._sharedBrush = fn.mkBrush(brush) self._brushes = None else: # brushes configured, ignore single brush (if any) brushes = [] for brush in self.opts['brushes']: if not isinstance(brush, QtGui.QBrush): brush = fn.mkBrush(brush) brushes.append(brush) self._sharedBrush = None self._brushes = brushes self._singleColor = ( self._sharedPen is not None and self._sharedBrush is not None ) def _getNormalizedCoords(self): def asarray(x): if x is None or np.isscalar(x) or isinstance(x, np.ndarray): return x return np.array(x) x = asarray(self.opts.get('x')) x0 = asarray(self.opts.get('x0')) x1 = asarray(self.opts.get('x1')) width = asarray(self.opts.get('width')) if x0 is None: if width is None: raise Exception('must specify either x0 or width') if x1 is not None: x0 = x1 - width elif x is not None: x0 = x - width/2. else: raise Exception('must specify at least one of x, x0, or x1') if width is None: if x1 is None: raise Exception('must specify either x1 or width') width = x1 - x0 y = asarray(self.opts.get('y')) y0 = asarray(self.opts.get('y0')) y1 = asarray(self.opts.get('y1')) height = asarray(self.opts.get('height')) if y0 is None: if height is None: y0 = 0 elif y1 is not None: y0 = y1 - height elif y is not None: y0 = y - height/2. else: y0 = 0 if height is None: if y1 is None: raise Exception('must specify either y1 or height') height = y1 - y0 # ensure x0 < x1 and y0 < y1 t0, t1 = x0, x0 + width x0 = np.minimum(t0, t1, dtype=np.float64) x1 = np.maximum(t0, t1, dtype=np.float64) t0, t1 = y0, y0 + height y0 = np.minimum(t0, t1, dtype=np.float64) y1 = np.maximum(t0, t1, dtype=np.float64) # here, all of x0, y0, x1, y1 are numpy objects, # BUT could possibly be numpy scalars return x0, y0, x1, y1 def _prepareData(self): x0, y0, x1, y1 = self._getNormalizedCoords() if x0.size == 0 or y0.size == 0: self._dataBounds = (None, None), (None, None) self._rectarray.resize(0) return xmn, xmx = np.min(x0), np.max(x1) ymn, ymx = np.min(y0), np.max(y1) self._dataBounds = (xmn, xmx), (ymn, ymx) self._rectarray.resize(max(x0.size, y0.size)) memory = self._rectarray.ndarray() memory[:, 0] = x0 memory[:, 1] = y0 memory[:, 2] = x1 - x0 memory[:, 3] = y1 - y0 def _render(self, painter): multi_pen = self._pens is not None multi_brush = self._brushes is not None no_pen = ( not multi_pen and self._sharedPen.style() == QtCore.Qt.PenStyle.NoPen ) rects = self._rectarray.instances() if no_pen and multi_brush: for idx, rect in enumerate(rects): painter.fillRect(rect, self._brushes[idx]) else: if not multi_pen: painter.setPen(self._sharedPen) if not multi_brush: painter.setBrush(self._sharedBrush) for idx, rect in enumerate(rects): if multi_pen: painter.setPen(self._pens[idx]) if multi_brush: painter.setBrush(self._brushes[idx]) painter.drawRect(rect) def drawPicture(self): self.picture = QtGui.QPicture() painter = QtGui.QPainter(self.picture) self._render(painter) painter.end() def paint(self, p, *args): if self._singleColor: p.setPen(self._sharedPen) p.setBrush(self._sharedBrush) drawargs = self._rectarray.drawargs() p.drawRects(*drawargs) else: if self.picture is None: self.drawPicture() self.picture.play(p) def shape(self): if self._shape is None: shape = QtGui.QPainterPath() rects = self._rectarray.instances() for rect in rects: shape.addRect(rect) self._shape = shape return self._shape def implements(self, interface=None): ints = ['plotData'] if interface is None: return ints return interface in ints def name(self): return self.opts.get('name', None) def getData(self): return self.opts.get('x'), self.opts.get('height') def dataBounds(self, ax, frac=1.0, orthoRange=None): # _penWidth is available after _updateColors() pw = self._penWidth[0] * 0.5 # _dataBounds is available after _prepareData() bounds = self._dataBounds[ax] if bounds[0] is None or bounds[1] is None: return None, None return (bounds[0] - pw, bounds[1] + pw) def pixelPadding(self): # _penWidth is available after _updateColors() pw = (self._penWidth[1] or 1) * 0.5 return pw def boundingRect(self): xmn, xmx = self.dataBounds(ax=0) if xmn is None or xmx is None: return QtCore.QRectF() ymn, ymx = self.dataBounds(ax=1) if ymn is None or ymx is None: return QtCore.QRectF() px = py = 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 return QtCore.QRectF(xmn-px, ymn-py, (2*px)+xmx-xmn, (2*py)+ymx-ymn)