qml
Creazione di elementi personalizzati in C ++
Ricerca…
Creazione di elementi personalizzati in C ++
QML è venuto con una ricca serie di elementi visivi. Usando solo QML possiamo costruire applicazioni complesse con questi elementi. Inoltre è molto facile creare il proprio elemento basato su set di elementi standard come Rettangolo, Pulsante, Immagine ecc. Inoltre, possiamo usare elementi come Canvas per costruire elementi con la pittura personalizzata. Sembrerebbe che possiamo costruire una varietà di applicazioni solo in QML, senza toccare le capacità del C ++. Ed è vero, ma a volte vorremmo rendere più veloce la nostra applicazione o vogliamo estenderla con la potenza di Qt o aggiungere alcune opportunità che non sono disponibili in QML. E certamente c'è una tale possibilità in QML . Fondamentalmente QtQuick usa Scene Graph per dipingere il suo contenuto un motore di rendering ad alte prestazioni basato su OpenGL . Per implementare il nostro elemento visivo possiamo usare 2 modi:
- Il tradizionale per Qt way usando QPainter ( QQuickPaintedItem ).
- La modalità QML comune che utilizza le funzionalità QQuickItem e OpenGL.
È possibile che il primo metodo appaia più semplice, ma vale la pena considerare che è anche più lento del primo poiché QtQuick dipinge il contenuto dell'oggetto su una superficie e quindi lo inserisce nel grafico di scena in modo che il rendering sia un'operazione a due passaggi. Quindi, utilizzare direttamente l'API del grafico di scena è sempre molto più veloce.
Per esplorare entrambi i metodi più vicini creiamo il nostro elemento che sicuramente non esiste in QML, ad esempio un triangolo.
Dichiarazione di 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();
};
Aggiungiamo la macro Q_OBJECT per lavorare con i segnali. Inoltre, aggiungiamo proprietà personalizzate per specificare il colore del nostro rettangolo. Per far funzionare tutto ciò di cui abbiamo bisogno è reimplementare la funzione virtuale QQuiclItem :: updatePaintNode () .
Implementazione di classe.
In primo luogo definiamo un costruttore.
QQuickCustomItem::QQuickCustomItem(QQuickItem *parent) :
QQuickItem(parent),
m_color(Qt::red),
m_needUpdate(true)
{
setFlag(QQuickItem::ItemHasContents);
}
Si noti che la chiamata alla funzione setFlag () è obbligatoria, altrimenti l'oggetto non verrà aggiunto al grafico della scena. Successivamente, definiamo una funzione per il dolore.
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;
}
Alla prima chiamata alla funzione il nostro nodo non è ancora stato creato, quindi oldNode sarà NULL. Quindi creiamo il nodo e assegniamo geometria e materiale ad esso. Qui usiamo GL_TRIANGLE_FAN per la nostra geometria per dipingere il rettangolo solido. Questo punto è lo stesso di OpenGL. Ad esempio per disegnare una cornice triangolare possiamo cambiare il codice in:
geometry->setDrawingMode(GL_LINE_LOOP);
geometry->setLineWidth(5);
È possibile fare riferimento al manuale OpenGL per controllare altre forme. Quindi, non resta che definire setter / getter per la nostra proprietà:
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();
}
}
Ora c'è solo un piccolo dettaglio per farlo funzionare. Dobbiamo notificare a QtQuick il nuovo elemento. Ad esempio, puoi aggiungere questo codice al tuo main.cpp:
qmlRegisterType<QQuickCustomItem>("stackoverflow.qml", 1, 0, "Triangle");
Ed ecco il nostro file di 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);
}
}
Come vedi il nostro oggetto si comporta come tutti gli altri oggetti QML. Ora creiamo lo stesso oggetto usando QPainter :
Tutto ciò di cui abbiamo bisogno è di sostituire
QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData);
con
void paint(QPainter *painter);
e, di cource ereditiamo la nostra classe da QQuickPaintedItem invece di QQuickItem . Ecco la nostra funzione di pittura:
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);
}
Tutto il resto rimane invariato.