Recherche…


Création d'éléments personnalisés en C ++

QML est venu avec un riche ensemble d'éléments visuels. En utilisant uniquement QML, nous pouvons créer des applications complexes avec ces éléments. En outre, il est très facile de créer votre propre élément en fonction d'un ensemble d'éléments standard tels que Rectangle, Button, Image, etc. De plus, nous pouvons utiliser des éléments tels que Canvas pour créer des éléments avec une peinture personnalisée. Il semblerait que nous puissions créer diverses applications en QML uniquement, sans toucher aux fonctionnalités de C ++. Et c'est en fait vrai, mais parfois nous aimerions rendre notre application plus rapide ou nous voulons l'étendre avec la puissance de Qt ou pour ajouter des opportunités qui ne sont pas disponibles dans QML. Et il y a certainement une telle possibilité dans QML . Fondamentalement, QtQuick utilise Scene Graph pour peindre son contenu en un moteur de rendu hautes performances basé sur OpenGL . Pour implémenter notre propre élément visuel, nous pouvons utiliser 2 manières:

  1. La méthode traditionnelle pour Qt utilisant QPainter ( QQuickPaintedItem ).
  2. La méthode QML commune utilisant les fonctionnalités QQuickItem et OpenGL.

Il est possible que la première méthode semble plus facile, mais cela vaut la peine de la considérer comme plus lente que la première, car QtQuick peint le contenu de l'élément sur une surface, puis l'insère dans un graphe de scène. L'utilisation directe de l'API de graphes de scènes est donc toujours beaucoup plus rapide.

Pour explorer les deux méthodes de plus près, créons notre propre élément qui n'existe pas dans QML, par exemple un triangle.

Déclaration de 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();
};

Nous ajoutons la macro Q_OBJECT pour travailler avec les signaux. Nous ajoutons également une propriété personnalisée pour spécifier la couleur de notre rectangle. Pour que cela fonctionne, il suffit de réimplémenter la fonction virtuelle QQuiclItem :: updatePaintNode () .

Implémentation de classe.

Nous définissons tout d'abord un constructeur.

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

Veuillez noter que l'appel de la fonction setFlag () est obligatoire, sinon votre objet ne sera pas ajouté au graphique de la scène. Ensuite, nous définissons une fonction pour la douleur.

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

Au premier appel à la fonction, notre noeud n'est pas encore créé, donc oldNode sera NULL. Nous créons donc le nœud et lui assignons géométrie et matériel. Ici, nous utilisons GL_TRIANGLE_FAN pour notre géométrie pour peindre un rectangle plein. Ce point est le même que dans OpenGL. Par exemple, pour dessiner un triangle, nous pouvons changer le code pour:

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

Vous pouvez vous référer au manuel OpenGL pour vérifier d'autres formes. Donc, il ne reste plus qu'à définir setter / getter pour notre propriété:

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

Maintenant, il n'y a qu'un petit détail pour que cela fonctionne. Nous devons notifier QtQuick du nouvel élément. Par exemple, vous pouvez ajouter ce code à votre main.cpp:

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

Et voici notre fichier de 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);
    }
}

Comme vous voyez notre article se comporte comme tous les autres éléments QML. Maintenant, créons le même élément en utilisant QPainter :

Tout ce dont nous avons besoin est de remplacer

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

avec

void paint(QPainter *painter);

et, bien sûr, hériter de notre classe de QQuickPaintedItem au lieu de QQuickItem . Voici notre fonction de peinture:

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

Tout le reste reste inchangé.



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow