Skip to main content
`Ctrl`+`K`

# 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']

    class BarGraphItem(GraphicsObject):

        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)