qml
Benutzerdefinierte Elemente in C ++ erstellen
Suche…
Benutzerdefinierte Elemente in C ++ erstellen
QML enthielt zahlreiche visuelle Elemente. Nur mit QML können wir komplexe Anwendungen mit diesen Elementen erstellen. Es ist auch sehr einfach, ein eigenes Element basierend auf einer Reihe von Standardelementen wie Rechteck, Schaltfläche, Bild usw. zu erstellen. Darüber hinaus können Elemente wie Canvas zum Erstellen von Elementen mit benutzerdefinierten Malereien verwendet werden. Es scheint, dass wir eine Vielzahl von Anwendungen nur in QML erstellen können, ohne die Möglichkeiten von C ++ zu beeinträchtigen. Das stimmt zwar, aber manchmal möchten wir unsere Anwendung schneller machen oder mit Qt erweitern oder einige Möglichkeiten hinzufügen, die in QML nicht verfügbar sind. Und sicherlich gibt es eine solche Möglichkeit in QML . Grundsätzlich verwendet QtQuick Scene Graph , um eine hochleistungsfähige Rendering-Engine auf OpenGL- Basis zu erstellen . Um unser eigenes visuelles Element zu implementieren, haben wir zwei Möglichkeiten:
- Die traditionelle Vorgehensweise für Qt mit QPainter ( QQuickPaintedItem ).
- Die gängige QML-Methode mit QQuickItem- und OpenGL-Funktionalität.
Möglicherweise scheint die erste Methode einfacher zu sein, aber es lohnt sich zu berücksichtigen, dass sie auch langsamer ist als die erste, da QtQuick den Inhalt des Elements auf eine Oberfläche malt und dann in das Szenendiagramm einfügt, sodass das Rendern in zwei Schritten erfolgt. Die direkte Verwendung der Szenendiagramm-API ist daher immer wesentlich schneller.
Um beide Methoden näher kennenzulernen, erstellen wir ein eigenes Element, das in QML definitiv nicht vorhanden ist, beispielsweise ein Dreieck.
Klassenerklärung
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();
};
Wir fügen das Makro Q_OBJECT hinzu , um mit Signalen zu arbeiten. Außerdem fügen wir benutzerdefinierte Eigenschaften hinzu, um die Farbe unseres Rechtecks festzulegen. Damit alles funktioniert, müssen wir die virtuelle Funktion QQuiclItem :: updatePaintNode () erneut implementieren.
Klassenimplementierung
Zunächst definieren wir einen Konstruktor.
QQuickCustomItem::QQuickCustomItem(QQuickItem *parent) :
QQuickItem(parent),
m_color(Qt::red),
m_needUpdate(true)
{
setFlag(QQuickItem::ItemHasContents);
}
Bitte beachten Sie, dass der Funktionsaufruf setFlag () obligatorisch ist, andernfalls wird Ihr Objekt nicht zum Szenendiagramm hinzugefügt. Als Nächstes definieren wir eine Funktion für das Malen.
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;
}
Beim ersten Aufruf der Funktion wird unser Knoten noch nicht erstellt, sodass oldNode NULL ist. Also erstellen wir den Knoten und weisen ihm Geometrie und Material zu. Hier verwenden wir GL_TRIANGLE_FAN für unsere Geometrie, um ein Vollrechteck zu zeichnen. Dieser Punkt ist derselbe wie in OpenGL. Um zum Beispiel einen Dreieckrahmen zu zeichnen, können wir den Code folgendermaßen ändern:
geometry->setDrawingMode(GL_LINE_LOOP);
geometry->setLineWidth(5);
In dem OpenGL- Handbuch können Sie nach anderen Formen suchen. Es bleibt also nur noch Setter / Getter für unser Eigentum zu definieren:
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();
}
}
Jetzt gibt es nur noch ein kleines Detail, damit es funktioniert. Wir müssen QtQuick über den neuen Artikel informieren. Sie können diesen Code beispielsweise zu Ihrer main.cpp hinzufügen:
qmlRegisterType<QQuickCustomItem>("stackoverflow.qml", 1, 0, "Triangle");
Und hier ist unsere QML-Testdatei:
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);
}
}
Wie Sie sehen, verhält sich unser Artikel wie alle anderen QML-Artikel. Lassen Sie uns nun dasselbe Element mit QPainter erstellen :
Alles, was wir brauchen, ist zu ersetzen
QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData);
mit
void paint(QPainter *painter);
und natürlich erbt unsere Klasse von QQuickPaintedItem statt von QQuickItem . Hier ist unsere Malfunktion:
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);
}
Alles andere bleibt unverändert.