banner

Text Balloon in a QGraphicsView

December 12, 2012

Lately I have been doing some development using the Qt Framework and the PySide Python bindings. For my application I wanted a nice looking text balloon to label some points on a graph. First I subclassed QGraphicsTextItem and wrote new boundingRect() and paint() methods. The balloon can appear either below or above the point on the graph, so the bounding rect needs to be adjusted to handle this extra height.

Here is the relevant portion of the class:

class GraphicsBalloonTextItem(QtGui.QGraphicsTextItem):
    """
    This class draws a nice text balloon. The balloon can be
    pointing above or below depending upon the orientation attribute.
    """
    def __init__(self, parent, orientation='above'):
        super(GraphicsBalloonTextItem, self).__init__(parent)
        self.orientation = orientation

    def boundingRect(self):
        """
        Returns the new bounding rect which makes room for the
        balloon label and arrow.
        """
        rect = super(GraphicsBalloonTextItem, self).boundingRect()
        if self.orientation == 'above':
            # balloon above the point
            return rect.adjusted(-1, 0, 1, 12)
        else:
            # balloon below the point
            return rect.adjusted(-1, -12, 1, 0)

    def paint(self, painter, option, widget):
        """
        This method does the drawing.
        """
        painter.setPen(QtCore.Qt.darkGray)
        painter.setBrush(QtGui.QColor(250, 245, 209))
        adjustedRect = self.boundingRect() # the rectangle around the text
        if self.orientation == 'above':
            # should draw the label balloon above the point
            adjustedRect.adjust(0, 0, 0, -12)
            vertices = [QtCore.QPointF(adjustedRect.width()/2 - 6,
                                       adjustedRect.height() - 1),
                        QtCore.QPointF(adjustedRect.width()/2,
                                       adjustedRect.height() + 12),
                        QtCore.QPointF(adjustedRect.width()/2 + 6,
                                       adjustedRect.height() - 1)]
        else:
            # should draw the label balloon below the point
            adjustedRect.adjust(0, 12, 0, 0)
            vertices = [QtCore.QPointF(adjustedRect.width()/2 - 6, 1),
                        QtCore.QPointF(adjustedRect.width()/2, -12),
                        QtCore.QPointF(adjustedRect.width()/2 + 6, 1)]

        # paint the balloon rectangle
        painter.drawRoundedRect(adjustedRect, 8, 8)

        # paint the balloon arrow triangle fill
        painter.setPen(QtCore.Qt.NoPen)
        painter.drawPolygon(vertices)

        # paint the balloon arrow triangle stroke
        painter.setPen(QtCore.Qt.darkGray)
        painter.drawLine(vertices[0], vertices[1])
        painter.drawLine(vertices[2], vertices[1])

        # Finally call the parent paint method to draw the actual text
        super(GraphicsBalloonTextItem, self).paint(painter, option, widget)

Tagged

programming python

Related

← Time Lapse Assembler AWK Linear Regression →