Sök…


Skapa anpassade element i C ++

QML kom med en rik uppsättning visuella element. Med endast QML kan vi bygga komplexa applikationer med dessa element. Det är också mycket enkelt att bygga ditt eget element baserat på uppsättning standardobjekt som rektangel, knapp, bild etc. Dessutom kan vi använda objekt som Canvas för att bygga element med anpassad målning. Det verkar som om vi bara kan bygga en mängd olika applikationer i QML, utan att beröra C ++. Och det är faktiskt sant men ändå skulle vi ibland vilja göra vår ansökan snabbare eller vi vill utöka den med kraft av Qt eller lägga till några möjligheter som inte är tillgängliga i QML. Och det finns säkert en sådan möjlighet i QML . I grund och botten använder QtQuick Scene Graph för att måla sitt innehåll som en högpresterande återgivningsmotor baserad på OpenGL . För att implementera vårt eget visuella element kan vi använda två sätt:

  1. Det traditionella för Qt-sätt med QPainter ( QQuickPaintedItem ).
  2. Det vanliga QML-sättet med QQuickItem och OpenGL-funktionalitet.

Det är möjligt att den första metoden verkar enklare men det är värt att tänka på att det också är långsammare än den första eftersom QtQuick målar objektets innehåll på en yta och sedan sätter in det i scengrafen så att rendering är en tvåstegsoperation. Så att använda scengraf API direkt är alltid betydligt snabbare.

För att utforska båda metoderna närmare, låt oss skapa vårt eget element som definitivt inte finns i QML, till exempel en triangel.

Klassdeklaration

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

Vi lägger till Q_OBJECT- makro för att arbeta med signaler. Vi lägger också till egendefinierad egenskap för att ange färg på vår rektangel. För att göra det fungerar allt vi behöver är att implementera den virtuella funktionen QQuiclItem :: updatePaintNode () igen .

Klassimplementering.

Först definierar vi en konstruktör.

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

Observera att setFlag () -samtalet är obligatoriskt, annars kommer ditt objekt inte att läggas till scengrafen. Därefter definierar vi en funktion för smärta.

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

Vid det första samtalet till funktionen är vår nod ännu inte skapad så oldNode blir NULL. Så vi skapar noden och tilldelar geometri och material till den. Här använder vi GL_TRIANGLE_FAN för vår geometri för att måla fast rektangel. Denna punkt är densamma som i OpenGL. Till exempel för att rita triangelram kan vi ändra koden till:

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

Du kan hänvisa till OpenGL- manualen för att leta efter andra former. Så, allt som återstår är att definiera setter / getter för vår egendom:

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

Nu finns det bara en liten detalj för att det ska fungera. Vi måste meddela QtQuick om den nya artikeln. Till exempel kan du lägga till den här koden till din main.cpp:

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

Och här är vår QML-testfil:

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

Som ni ser uppträder vårt objekt som alla andra QML-objekt. Låt oss nu skapa samma objekt med QPainter :

Allt vi behöver är att ersätta

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

med

void paint(QPainter *painter);

och för att ärva vår klass från QQuickPaintedItem istället för QQuickItem . Här är vår målningsfunktion:

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

Allt annat förblir oförändrat.



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow