Ricerca…


Creazione di elementi personalizzati in C ++

QML è venuto con una ricca serie di elementi visivi. Usando solo QML possiamo costruire applicazioni complesse con questi elementi. Inoltre è molto facile creare il proprio elemento basato su set di elementi standard come Rettangolo, Pulsante, Immagine ecc. Inoltre, possiamo usare elementi come Canvas per costruire elementi con la pittura personalizzata. Sembrerebbe che possiamo costruire una varietà di applicazioni solo in QML, senza toccare le capacità del C ++. Ed è vero, ma a volte vorremmo rendere più veloce la nostra applicazione o vogliamo estenderla con la potenza di Qt o aggiungere alcune opportunità che non sono disponibili in QML. E certamente c'è una tale possibilità in QML . Fondamentalmente QtQuick usa Scene Graph per dipingere il suo contenuto un motore di rendering ad alte prestazioni basato su OpenGL . Per implementare il nostro elemento visivo possiamo usare 2 modi:

  1. Il tradizionale per Qt way usando QPainter ( QQuickPaintedItem ).
  2. La modalità QML comune che utilizza le funzionalità QQuickItem e OpenGL.

È possibile che il primo metodo appaia più semplice, ma vale la pena considerare che è anche più lento del primo poiché QtQuick dipinge il contenuto dell'oggetto su una superficie e quindi lo inserisce nel grafico di scena in modo che il rendering sia un'operazione a due passaggi. Quindi, utilizzare direttamente l'API del grafico di scena è sempre molto più veloce.

Per esplorare entrambi i metodi più vicini creiamo il nostro elemento che sicuramente non esiste in QML, ad esempio un triangolo.

Dichiarazione di classe

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();
};

Aggiungiamo la macro Q_OBJECT per lavorare con i segnali. Inoltre, aggiungiamo proprietà personalizzate per specificare il colore del nostro rettangolo. Per far funzionare tutto ciò di cui abbiamo bisogno è reimplementare la funzione virtuale QQuiclItem :: updatePaintNode () .

Implementazione di classe.

In primo luogo definiamo un costruttore.

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

Si noti che la chiamata alla funzione setFlag () è obbligatoria, altrimenti l'oggetto non verrà aggiunto al grafico della scena. Successivamente, definiamo una funzione per il dolore.

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;
}

Alla prima chiamata alla funzione il nostro nodo non è ancora stato creato, quindi oldNode sarà NULL. Quindi creiamo il nodo e assegniamo geometria e materiale ad esso. Qui usiamo GL_TRIANGLE_FAN per la nostra geometria per dipingere il rettangolo solido. Questo punto è lo stesso di OpenGL. Ad esempio per disegnare una cornice triangolare possiamo cambiare il codice in:

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

È possibile fare riferimento al manuale OpenGL per controllare altre forme. Quindi, non resta che definire setter / getter per la nostra proprietà:

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();
    }
}

Ora c'è solo un piccolo dettaglio per farlo funzionare. Dobbiamo notificare a QtQuick il nuovo elemento. Ad esempio, puoi aggiungere questo codice al tuo main.cpp:

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

Ed ecco il nostro file di test 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);
    }
}

Come vedi il nostro oggetto si comporta come tutti gli altri oggetti QML. Ora creiamo lo stesso oggetto usando QPainter :

Tutto ciò di cui abbiamo bisogno è di sostituire

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

con

void paint(QPainter *painter);

e, di cource ereditiamo la nostra classe da QQuickPaintedItem invece di QQuickItem . Ecco la nostra funzione di pittura:

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);
}

Tutto il resto rimane invariato.



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow