Поиск…


Создание пользовательских элементов в C ++

QML поставляется с богатым набором визуальных элементов. Используя только QML, мы можем создавать сложные приложения с этими элементами. Также очень легко создать собственный элемент на основе набора стандартных элементов, таких как Rectangle, Button, Image и т. Д. Кроме того, мы можем использовать элементы, такие как Canvas, для создания элемента с пользовательской живописью. Казалось бы, мы можем создавать разнообразные приложения только в QML, не затрагивая возможности C ++. И на самом деле это правда, но иногда мы хотели бы сделать наше приложение быстрее или мы хотим расширить его с помощью Qt или добавить некоторые возможности, недоступные в QML. И, конечно, такая возможность существует в QML . В основном QtQuick использует Scene Graph для рисования своего контента высокопроизводительным движком рендеринга на основе OpenGL . Чтобы реализовать наш собственный визуальный элемент, мы можем использовать два способа:

  1. Традиционный способ Qt с использованием QPainter ( QQuickPaintedItem ).
  2. Общий способ QML с использованием функций QQuickItem и OpenGL.

Возможно, первый способ кажется более простым, но стоит подумать, что он также медленнее первого, так как QtQuick рисует содержимое элемента на поверхности, а затем вставляет его в граф сцены, поэтому рендеринг является двухступенчатой. Таким образом, использование API графа сцены всегда значительно быстрее.

Чтобы лучше изучить оба метода, создадим наш собственный элемент, который определенно не существует в QML, например треугольник.

Объявление класса

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

Мы добавляем макрос Q_OBJECT для работы с сигналами. Также мы добавляем настраиваемое свойство, чтобы указать цвет нашего прямоугольника. Чтобы он работал, все, что нам нужно, это переопределить виртуальную функцию QQuiclItem :: updatePaintNode () .

Выполнение класса.

Сначала мы определяем конструктор.

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

Обратите внимание, что вызов функции setFlag () является обязательным, иначе ваш объект не будет добавлен в график сцены. Затем мы определяем функцию для лечения.

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

При первом вызове функции наш узел еще не создан, поэтому oldNode будет NULL. Поэтому мы создаем узел и присваиваем ему геометрию и материал. Здесь мы используем GL_TRIANGLE_FAN для нашей геометрии для рисования сплошного прямоугольника. Этот момент тот же, что и в OpenGL. Например, чтобы нарисовать рамку треугольника, мы можем изменить код на:

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

Вы можете обратиться к руководству OpenGL для проверки других фигур. Итак, остается только определить setter / getter для нашего свойства:

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

Теперь есть только одна небольшая деталь, чтобы заставить ее работать. Нам нужно уведомить QtQuick о новом элементе. Например, вы можете добавить этот код в свой файл main.cpp:

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

И вот наш тестовый файл 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);
    }
}

Как вы видите, наш элемент ведет себя как все другие элементы QML. Теперь давайте создадим тот же элемент с помощью QPainter :

Все, что нам нужно, это заменить

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

с

void paint(QPainter *painter);

и, cource наследуют наш класс от QQuickPaintedItem вместо QQuickItem . Вот наша функция рисования:

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

Все остальное остается неизменным.



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow