qml
Creando elementos personalizados en C ++
Buscar..
Creando elementos personalizados en C ++
QML vino con un rico conjunto de elementos visuales. Usando solo QML podemos construir aplicaciones complejas con estos elementos. También es muy fácil crear su propio elemento basado en un conjunto de elementos estándar como Rectángulo, Botón, Imagen, etc. Además, podemos usar elementos como Canvas para crear elementos con pintura personalizada. Parece que podemos construir una variedad de aplicaciones solo en QML, sin tocar las capacidades de C ++. Y en realidad es cierto, pero aún así a veces nos gustaría que nuestra aplicación sea más rápida o queremos extenderla con el poder de Qt o agregar alguna oportunidad que no esté disponible en QML. Y ciertamente hay tal posibilidad en QML . Básicamente, QtQuick utiliza Scene Graph para pintar su contenido en un motor de renderizado de alto rendimiento basado en OpenGL . Para implementar nuestro propio elemento visual podemos usar 2 formas:
- La forma tradicional para Qt usando QPainter ( QQuickPaintedItem ).
- La forma QML común mediante el uso de la funcionalidad QQuickItem y OpenGL.
Es posible que el primer método parezca más fácil, pero vale la pena considerar que también es más lento que el primero, ya que QtQuick pinta el contenido del elemento en una superficie y luego lo inserta en el gráfico de la escena para que la representación sea una operación de dos pasos. Por lo tanto, usar la API de gráficos de escena directamente es siempre mucho más rápido.
Para explorar ambos métodos más cerca, creemos nuestro propio elemento que definitivamente no existe en QML, por ejemplo, un triángulo.
Declaración de clase
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();
};
Agregamos la macro Q_OBJECT para trabajar con señales. También agregamos propiedades personalizadas para especificar el color de nuestro rectángulo. Para que funcione, todo lo que necesitamos es volver a implementar la función virtual QQuiclItem :: updatePaintNode () .
Implementación de la clase.
En primer lugar definimos un constructor.
QQuickCustomItem::QQuickCustomItem(QQuickItem *parent) :
QQuickItem(parent),
m_color(Qt::red),
m_needUpdate(true)
{
setFlag(QQuickItem::ItemHasContents);
}
Tenga en cuenta que la llamada a la función setFlag () es obligatoria, de lo contrario su objeto no se agregará al gráfico de escena. A continuación, definimos una función para el dolor.
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;
}
En la primera llamada a la función, nuestro nodo aún no está creado, por lo que oldNode será NULL. Entonces creamos el nodo y le asignamos geometría y material. Aquí usamos GL_TRIANGLE_FAN para nuestra geometría para pintar un rectángulo sólido. Este punto es el mismo que en OpenGL. Por ejemplo, para dibujar un marco de triángulo podemos cambiar el código a:
geometry->setDrawingMode(GL_LINE_LOOP);
geometry->setLineWidth(5);
Puede consultar el manual de OpenGL para verificar otras formas. Entonces, todo lo que queda es definir el setter / getter para nuestra propiedad:
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();
}
}
Ahora solo hay un pequeño detalle para que funcione. Necesitamos notificar a QtQuick del nuevo artículo. Por ejemplo, puede agregar este código a su main.cpp:
qmlRegisterType<QQuickCustomItem>("stackoverflow.qml", 1, 0, "Triangle");
Y aquí está nuestro archivo de prueba 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);
}
}
Como ves, nuestro artículo se comporta como todos los demás elementos de QML. Ahora vamos a crear el mismo elemento usando QPainter :
Todo lo que necesitamos es reemplazar
QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData);
con
void paint(QPainter *painter);
y, por supuesto, heredamos nuestra clase de QQuickPaintedItem lugar de QQuickItem . Aquí está nuestra función de pintura:
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);
}
Todo lo demás permanece sin cambios.