Buscar..


Creando elementos personalizados en C ++

QML vino con un rico conjunto de elementos visuales. Usando solo QML podemos construir aplicaciones complejas con estos elementos. También es muy fácil crear su propio elemento basado en un conjunto de elementos estándar como Rectángulo, Botón, Imagen, etc. Además, podemos usar elementos como Canvas para crear elementos con pintura personalizada. Parece que podemos construir una variedad de aplicaciones solo en QML, sin tocar las capacidades de C ++. Y en realidad es cierto, pero aún así a veces nos gustaría que nuestra aplicación sea más rápida o queremos extenderla con el poder de Qt o agregar alguna oportunidad que no esté disponible en QML. Y ciertamente hay tal posibilidad en QML . Básicamente, QtQuick utiliza Scene Graph para pintar su contenido en un motor de renderizado de alto rendimiento basado en OpenGL . Para implementar nuestro propio elemento visual podemos usar 2 formas:

  1. La forma tradicional para Qt usando QPainter ( QQuickPaintedItem ).
  2. La forma QML común mediante el uso de la funcionalidad QQuickItem y OpenGL.

Es posible que el primer método parezca más fácil, pero vale la pena considerar que también es más lento que el primero, ya que QtQuick pinta el contenido del elemento en una superficie y luego lo inserta en el gráfico de la escena para que la representación sea una operación de dos pasos. Por lo tanto, usar la API de gráficos de escena directamente es siempre mucho más rápido.

Para explorar ambos métodos más cerca, creemos nuestro propio elemento que definitivamente no existe en QML, por ejemplo, un triángulo.

Declaración de clase

class QQuickCustomItem : public QQuickItem
{
    Q_OBJECT
    Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
public:
    QQuickCustomItem(QQuickItem *parent = Q_NULLPTR);

protected:
    QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData);

    QColor color() const;
    void setColor(const QColor &color);

private:
    QColor m_color;
    bool m_needUpdate;

signals:
    void colorChanged();
};

Agregamos la macro Q_OBJECT para trabajar con señales. También agregamos propiedades personalizadas para especificar el color de nuestro rectángulo. Para que funcione, todo lo que necesitamos es volver a implementar la función virtual QQuiclItem :: updatePaintNode () .

Implementación de la clase.

En primer lugar definimos un constructor.

QQuickCustomItem::QQuickCustomItem(QQuickItem *parent) :
    QQuickItem(parent),
    m_color(Qt::red),
    m_needUpdate(true)
{
    setFlag(QQuickItem::ItemHasContents);
}

Tenga en cuenta que la llamada a la función setFlag () es obligatoria, de lo contrario su objeto no se agregará al gráfico de escena. A continuación, definimos una función para el dolor.

QSGNode *QQuickCustomItem::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *updatePaintNodeData)
{
    Q_UNUSED(updatePaintNodeData)
    QSGGeometryNode *root = static_cast<QSGGeometryNode *>(oldNode);

    if(!root) {
        root = new QSGGeometryNode;
        QSGGeometry *geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 3);
        geometry->setDrawingMode(GL_TRIANGLE_FAN);
        geometry->vertexDataAsPoint2D()[0].set(width() / 2, 0);
        geometry->vertexDataAsPoint2D()[1].set(width(), height());
        geometry->vertexDataAsPoint2D()[2].set(0, height());

        root->setGeometry(geometry);
        root->setFlag(QSGNode::OwnsGeometry);
        root->setFlag(QSGNode::OwnsMaterial);
    }

    if(m_needUpdate) {
        QSGFlatColorMaterial *material = new QSGFlatColorMaterial;
        material->setColor(m_color);
        root->setMaterial(material);
        m_needUpdate = false;
    }

    return root;
}

En la primera llamada a la función, nuestro nodo aún no está creado, por lo que oldNode será NULL. Entonces creamos el nodo y le asignamos geometría y material. Aquí usamos GL_TRIANGLE_FAN para nuestra geometría para pintar un rectángulo sólido. Este punto es el mismo que en OpenGL. Por ejemplo, para dibujar un marco de triángulo podemos cambiar el código a:

geometry->setDrawingMode(GL_LINE_LOOP);
geometry->setLineWidth(5);

Puede consultar el manual de OpenGL para verificar otras formas. Entonces, todo lo que queda es definir el setter / getter para nuestra propiedad:

QColor QQuickCustomItem::color() const
{
    return m_color;
}

void QQuickCustomItem::setColor(const QColor &color)
{
    if(m_color != color) {
        m_color = color;
        m_needUpdate = true;
        update();
        colorChanged();
    }
}

Ahora solo hay un pequeño detalle para que funcione. Necesitamos notificar a QtQuick del nuevo artículo. Por ejemplo, puede agregar este código a su main.cpp:

qmlRegisterType<QQuickCustomItem>("stackoverflow.qml", 1, 0, "Triangle");

Y aquí está nuestro archivo de prueba QML:

import QtQuick 2.7
import QtQuick.Window 2.0
import stackoverflow.qml 1.0

Window {
    width: 800
    height: 800
    visible: true

    Rectangle {
        width: 200
        height: 200
        anchors.centerIn: parent
        color: "lightgrey"

        Triangle {
            id: rect
            width: 200
            height: 200
            transformOrigin: Item.Top
            color: "green"
            onColorChanged: console.log("color was changed");
            PropertyAnimation on rotation {
                from: 0
                to: 360
                duration: 5000
                loops: Animation.Infinite
            }
        }
    }
    Timer {
        interval: 1000
        repeat: true
        running: true
        onTriggered: rect.color = Qt.rgba(Math.random(),Math.random(),Math.random(),1);
    }
}

Como ves, nuestro artículo se comporta como todos los demás elementos de QML. Ahora vamos a crear el mismo elemento usando QPainter :

Todo lo que necesitamos es reemplazar

QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData);

con

void paint(QPainter *painter);

y, por supuesto, heredamos nuestra clase de QQuickPaintedItem lugar de QQuickItem . Aquí está nuestra función de pintura:

void QQuickCustomItem::paint(QPainter *painter)
{
    QPainterPath path;
    path.moveTo(width() / 2, 0);
    path.lineTo(width(), height());
    path.lineTo(0, height());
    path.lineTo(width() / 2, 0);
    painter->fillPath(path, m_color);
}

Todo lo demás permanece sin cambios.



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow