En esta página

Gráfico de escena - Elemento pintado

Muestra cómo implementar elementos de gráfico de escena personalizados basados en QPainter.

El ejemplo de elemento pintado muestra cómo utilizar el marco de gráficos de escena QML para implementar elementos de gráficos de escena personalizados utilizando QPainter.

La clase QQuickPaintedItem es una clase derivada de QQuickItem para implementar elementos de gráfico de escena QML personalizados utilizando las interfaces QPainter.

El ejemplo consiste en una clase de elemento y un archivo QML para utilizar el elemento. La clase TextBalloon representa los globos de texto individuales extendiendo QQuickPaintedItem y el archivo textballoons.qml se utiliza para cargar el módulo que contiene el tipo QML TextBalloon y mostrar los globos de texto.

Nos centraremos primero en la clase TextBalloon y continuaremos con el archivo textballoons.qml. Para ver un ejemplo de cómo implementar un plugin para un módulo QML, consulta Escribiendo un Plugin de Extensión.

Declaración de la clase TextBalloon

La clase TextBalloon hereda de QQuickPaintedItem. QQuickPaintedItem es la clase base para todos los elementos basados en QPainter en el marco QML Scene Graph.

class TextBalloon : public QQuickPaintedItem
{
    Q_OBJECT
    Q_PROPERTY(bool rightAligned READ isRightAligned WRITE setRightAligned NOTIFY rightAlignedChanged)
    QML_ELEMENT

    public:
        TextBalloon(QQuickItem *parent = nullptr);
        void paint(QPainter *painter) override;

        bool isRightAligned() const;
        void setRightAligned(bool rightAligned);

    private:
        bool rightAligned;

    signals:
        void rightAlignedChanged();
};

Para implementar un QQuickPaintedItem debes implementar la función virtual pura de QQuickPaintedIem paint() que implementa el pintado de la clase.

Definición de la Clase TextBalloon

Tenemos que asegurarnos de inicializar la propiedad rightAligned para un elemento TextBalloon.

TextBalloon::TextBalloon(QQuickItem *parent)
    : QQuickPaintedItem(parent)
    , rightAligned(false)
{
}

Luego implementamos la función paint() que es llamada automáticamente por el framework Scene Graph para pintar el contenido del ítem. La función pinta el elemento en coordenadas locales.

void TextBalloon::paint(QPainter *painter)
{
    QBrush brush(QColor("#007430"));

    painter->setBrush(brush);
    painter->setPen(Qt::NoPen);
    painter->setRenderHint(QPainter::Antialiasing);

    QSizeF itemSize = size();
    painter->drawRoundedRect(0, 0, itemSize.width(), itemSize.height() - 10, 10, 10);

    if (rightAligned)
    {
        const QPointF points[3] = {
            QPointF(itemSize.width() - 10.0, itemSize.height() - 10.0),
            QPointF(itemSize.width() - 20.0, itemSize.height()),
            QPointF(itemSize.width() - 30.0, itemSize.height() - 10.0),
        };
        painter->drawConvexPolygon(points, 3);
    }
    else
    {
        const QPointF points[3] = {
            QPointF(10.0, itemSize.height() - 10.0),
            QPointF(20.0, itemSize.height()),
            QPointF(30.0, itemSize.height() - 10.0),
        };
        painter->drawConvexPolygon(points, 3);
    }
}

Comenzamos con la configuración de la pluma y el pincel en el elemento para definir el aspecto del elemento. Después empezamos a dibujar. Tenga en cuenta que el contentsBoundingRect() elemento se llama a dibujar en función del tamaño del elemento. El rectángulo devuelto por la función contentsBoundingRect() es el tamaño del elemento definido en el archivo QML.

Archivo textballoons.qml

La interfaz consta de dos partes principales. El área desplazable con los globos de texto y el botón de control para añadir nuevos globos.

BalloonView
ListModel {
    id: balloonModel
    ListElement {
        balloonWidth: 200
    }
    ListElement {
        balloonWidth: 120
    }
}

ListView {
    id: balloonView
    anchors.bottom: controls.top
    anchors.bottomMargin: 2
    anchors.top: parent.top
    delegate: TextBalloon {
        anchors.right: index % 2 !== 0 ? parent?.right : undefined
        height: 60
        rightAligned: index % 2 !== 0
        width: balloonWidth
    }
    model: balloonModel
    spacing: 5
    width: parent.width
}

El balloonModel contiene dos tipos al inicio de la aplicación que serán mostrados por el balloonView. El balloonView alterna los elementos delegados TextBalloon entre alineado a la izquierda y alineado a la derecha.

Controles
Rectangle {
    id: controls

    anchors.bottom: parent.bottom
    anchors.left: parent.left
    anchors.margins: 1
    anchors.right: parent.right
    border.width: 2
    color: "white"
    height: parent.height * 0.15

    Text {
        anchors.centerIn: parent
        text: qsTr("Add another balloon")
    }

    MouseArea {
        anchors.fill: parent
        hoverEnabled: true
        onClicked: {
            balloonModel.append({"balloonWidth": Math.floor(Math.random() * 200 + 100)})
            balloonView.positionViewAtIndex(balloonView.count -1, ListView.End)
        }
        onEntered: {
            parent.color = "#8ac953"
        }
        onExited: {
            parent.color = "white"
        }
    }
}

La parte de controles de la interfaz de usuario contiene un rectángulo con un MouseArea que cambia de color cuando el ratón pasa sobre él. Este control 'botón' añade un nuevo objeto al final del modelo con un ancho aleatorio.

Proyecto de ejemplo @ code.qt.io

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.