Suche…


Benutzerdefinierte Elemente in C ++ erstellen

QML enthielt zahlreiche visuelle Elemente. Nur mit QML können wir komplexe Anwendungen mit diesen Elementen erstellen. Es ist auch sehr einfach, ein eigenes Element basierend auf einer Reihe von Standardelementen wie Rechteck, Schaltfläche, Bild usw. zu erstellen. Darüber hinaus können Elemente wie Canvas zum Erstellen von Elementen mit benutzerdefinierten Malereien verwendet werden. Es scheint, dass wir eine Vielzahl von Anwendungen nur in QML erstellen können, ohne die Möglichkeiten von C ++ zu beeinträchtigen. Das stimmt zwar, aber manchmal möchten wir unsere Anwendung schneller machen oder mit Qt erweitern oder einige Möglichkeiten hinzufügen, die in QML nicht verfügbar sind. Und sicherlich gibt es eine solche Möglichkeit in QML . Grundsätzlich verwendet QtQuick Scene Graph , um eine hochleistungsfähige Rendering-Engine auf OpenGL- Basis zu erstellen . Um unser eigenes visuelles Element zu implementieren, haben wir zwei Möglichkeiten:

  1. Die traditionelle Vorgehensweise für Qt mit QPainter ( QQuickPaintedItem ).
  2. Die gängige QML-Methode mit QQuickItem- und OpenGL-Funktionalität.

Möglicherweise scheint die erste Methode einfacher zu sein, aber es lohnt sich zu berücksichtigen, dass sie auch langsamer ist als die erste, da QtQuick den Inhalt des Elements auf eine Oberfläche malt und dann in das Szenendiagramm einfügt, sodass das Rendern in zwei Schritten erfolgt. Die direkte Verwendung der Szenendiagramm-API ist daher immer wesentlich schneller.

Um beide Methoden näher kennenzulernen, erstellen wir ein eigenes Element, das in QML definitiv nicht vorhanden ist, beispielsweise ein Dreieck.

Klassenerklärung

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

Wir fügen das Makro Q_OBJECT hinzu , um mit Signalen zu arbeiten. Außerdem fügen wir benutzerdefinierte Eigenschaften hinzu, um die Farbe unseres Rechtecks ​​festzulegen. Damit alles funktioniert, müssen wir die virtuelle Funktion QQuiclItem :: updatePaintNode () erneut implementieren.

Klassenimplementierung

Zunächst definieren wir einen Konstruktor.

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

Bitte beachten Sie, dass der Funktionsaufruf setFlag () obligatorisch ist, andernfalls wird Ihr Objekt nicht zum Szenendiagramm hinzugefügt. Als Nächstes definieren wir eine Funktion für das Malen.

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

Beim ersten Aufruf der Funktion wird unser Knoten noch nicht erstellt, sodass oldNode NULL ist. Also erstellen wir den Knoten und weisen ihm Geometrie und Material zu. Hier verwenden wir GL_TRIANGLE_FAN für unsere Geometrie, um ein Vollrechteck zu zeichnen. Dieser Punkt ist derselbe wie in OpenGL. Um zum Beispiel einen Dreieckrahmen zu zeichnen, können wir den Code folgendermaßen ändern:

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

In dem OpenGL- Handbuch können Sie nach anderen Formen suchen. Es bleibt also nur noch Setter / Getter für unser Eigentum zu definieren:

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

Jetzt gibt es nur noch ein kleines Detail, damit es funktioniert. Wir müssen QtQuick über den neuen Artikel informieren. Sie können diesen Code beispielsweise zu Ihrer main.cpp hinzufügen:

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

Und hier ist unsere QML-Testdatei:

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

Wie Sie sehen, verhält sich unser Artikel wie alle anderen QML-Artikel. Lassen Sie uns nun dasselbe Element mit QPainter erstellen :

Alles, was wir brauchen, ist zu ersetzen

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

mit

void paint(QPainter *painter);

und natürlich erbt unsere Klasse von QQuickPaintedItem statt von QQuickItem . Hier ist unsere Malfunktion:

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

Alles andere bleibt unverändert.



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow