Szukaj…


Tworzenie niestandardowych elementów w C ++

QML przyszedł z bogatym zestawem elementów wizualnych. Używając tylko QML, możemy budować złożone aplikacje z tych elementów. Również bardzo łatwo jest zbudować własny element na podstawie zestawu standardowych elementów, takich jak Prostokąt, Przycisk, Obraz itp. Co więcej, możemy użyć przedmiotów takich jak Płótno, aby zbudować element z niestandardowym malowaniem. Wydaje się, że możemy budować różnorodne aplikacje tylko w języku QML, bez naruszania możliwości C ++. I to prawda, ale czasami chcielibyśmy przyspieszyć naszą aplikację lub chcemy rozszerzyć ją o moc Qt lub dodać jakieś możliwości, które nie są dostępne w QML. I z pewnością istnieje taka możliwość w QML . Zasadniczo QtQuick wykorzystuje Scene Graph do malowania zawartości wysoce wydajnym silnikiem renderującym opartym na OpenGL . Aby wdrożyć własny element wizualny, możemy użyć 2 sposobów:

  1. Tradycyjny sposób dla Qt przy użyciu QPainter ( QQuickPaintedItem ).
  2. Popularny sposób QML przy użyciu funkcji QQuickItem i OpenGL.

Możliwe, że pierwsza metoda wydaje się łatwiejsza, ale warto wziąć pod uwagę, że jest ona również wolniejsza niż pierwsza, ponieważ QtQuick maluje zawartość elementu na powierzchni, a następnie wstawia ją do wykresu sceny, aby rendering był operacją dwuetapową. Zatem bezpośrednie użycie interfejsu API wykresów scen jest zawsze znacznie szybsze.

Aby bliżej zbadać obie metody, stwórzmy własny element, który zdecydowanie nie istnieje w QML, na przykład trójkąt.

Deklaracja klasy

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

Dodajemy makro Q_OBJECT do pracy z sygnałami. Dodajemy również niestandardową właściwość, aby określić kolor naszego prostokąta. Aby działało , potrzebujemy ponownie zaimplementować funkcję wirtualną QQuiclItem :: updatePaintNode () .

Implementacja klasy.

Najpierw definiujemy konstruktor.

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

Należy pamiętać, że wywołanie funkcji setFlag () jest obowiązkowe, w przeciwnym razie obiekt nie zostanie dodany do wykresu sceny. Następnie definiujemy funkcję bólu.

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

Przy pierwszym wywołaniu funkcji nasz węzeł nie został jeszcze utworzony, więc oldNode będzie miał wartość NULL. Tworzymy więc węzeł i przypisujemy mu geometrię i materiał. Tutaj używamy GL_TRIANGLE_FAN dla naszej geometrii do malowania jednolitego prostokąta. Ten punkt jest taki sam jak w OpenGL. Na przykład, aby narysować ramkę trójkąta, możemy zmienić kod na:

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

Możesz sprawdzić instrukcję OpenGL, aby sprawdzić inne kształty. Pozostaje tylko zdefiniować setter / getter dla naszej właściwości:

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

Teraz jest tylko jeden mały szczegół, aby działał. Musimy powiadomić QtQuick o nowym elemencie. Na przykład możesz dodać ten kod do pliku main.cpp:

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

A oto nasz plik testowy 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);
    }
}

Jak widzisz, nasz produkt zachowuje się jak wszystkie inne elementy QML. Teraz stwórzmy ten sam element za pomocą QPainter :

Wszystko, czego potrzebujemy, to wymienić

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

z

void paint(QPainter *painter);

i, QQuickPaintedItem dziedziczymy naszą klasę po QQuickPaintedItem zamiast QQuickItem . Oto nasza funkcja malowania:

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

Wszystko inne pozostaje niezmienione.



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow