Exposición de atributos de tipos C++ a QML
QML puede ampliarse fácilmente con funcionalidad definida en código C++. Debido a la estrecha integración del motor QML con el sistema de metaobjetos Qt, cualquier funcionalidad que esté adecuadamente expuesta por una clase derivada de QObject o un tipo Q_GADGET es accesible desde el código QML. Esto permite que los datos y funciones de C++ sean accesibles directamente desde QML, a menudo con poca o ninguna modificación.
El motor QML tiene la capacidad de introspeccionar instancias de QObject a través del sistema de metaobjetos. Esto significa que cualquier código QML puede acceder a los siguientes miembros de una instancia de una clase derivada de QObject:
- Propiedades
- Métodos (siempre que sean ranuras públicas o estén marcados con Q_INVOKABLE)
- Señales
(Además, los enums están disponibles si se han declarado con Q_ENUM. Consulte Conversión de tipos de datos entre QML y C++ para obtener más detalles).
En general, se puede acceder a ellas desde QML independientemente de si una clase derivada de QObject se ha registrado en el sistema de tipos de QML. Sin embargo, si una clase se va a utilizar de un modo que requiera que el motor acceda a información de tipo adicional (por ejemplo, si la propia clase se va a utilizar como parámetro de método o propiedad, o si uno de sus tipos enum se va a utilizar de este modo), puede que sea necesario registrar la clase. Se recomienda registrar todos los tipos que se utilicen en QML, ya que sólo los tipos registrados pueden analizarse en tiempo de compilación.
El registro es necesario para los tipos Q_GADGET, ya que no derivan de una base común conocida y no pueden estar disponibles automáticamente. Sin registro, sus propiedades y métodos son inaccesibles.
Puedes hacer que los tipos C++ de un módulo diferente estén disponibles en tu propio módulo añadiendo una dependencia a tu llamada qt_add_qml_module utilizando la opción DEPENDENCIES. Por ejemplo, puede que desee depender de QtQuick para que sus tipos C++ expuestos en QML puedan utilizar QColor como argumentos de método y valores de retorno. QtQuick expone QColor como un color de tipo de valor. Estas dependencias pueden inferirse automáticamente en tiempo de ejecución, pero no debe confiar en ello.
Tenga en cuenta también que varios de los conceptos importantes tratados en este documento se demuestran en el tutorial Escribir extensiones QML con C++.
Para obtener más información sobre C++ y los distintos métodos de integración de QML, consulte la página de descripción general de la integración de C++ y QML.
Manejo y propiedad de tipos de datos
Cualquier dato que se transfiera de C++ a QML, ya sea como valor de propiedad, parámetro de método o valor de retorno, o valor de parámetro de señal, debe ser de un tipo admitido por el motor QML.
Por defecto, el motor admite una serie de tipos C++ de Qt y puede convertirlos automáticamente según convenga cuando se utilizan desde QML. Además, las clases C++ registradas en el sistema de tipos de QML pueden utilizarse como tipos de datos, al igual que sus enums, si se registran adecuadamente. Consulte Conversión de tipos de datos entre QML y C++ para obtener más información.
Además, las reglas de propiedad de datos se tienen en cuenta cuando los datos se transfieren de C++ a QML. Consulte Propiedad de datos para obtener más detalles.
Exposición de propiedades
Se puede especificar una propiedad para cualquier clase derivada de QObject utilizando la macro Q_PROPERTY(). Una propiedad es un miembro de datos de la clase con una función de lectura asociada y una función de escritura opcional.
Todas las propiedades de una clase derivada de QObject o Q_GADGET son accesibles desde QML.
Por ejemplo, a continuación se muestra una clase Message con una propiedad author. Como se especifica en la macro Q_PROPERTY, esta propiedad se puede leer a través del método author() y escribir a través del método setAuthor():
Nota: No utilice typedef o using para tipos Q_PROPERTY ya que confundirá a moc. Esto puede hacer que ciertas comparaciones de tipos fallen.
En lugar de:
using FooEnum = Foo::Enum;
class Bar : public QObject
{
Q_OBJECT
Q_PROPERTY(FooEnum enum READ enum WRITE setEnum NOTIFY enumChanged)
};Referirse al tipo directamente:
class Bar : public QObject { Q_OBJECT Q_PROPERTY(Foo::Enum enum READ enum WRITE setEnum NOTIFY enumChanged) };
Para que Message esté disponible es necesario utilizar QML_ELEMENT en C++ y qt_add_qml_module en CMake.
class Message : public QObject { Q_OBJECT QML_ELEMENT Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged) public: void setAuthor(const QString &a) { if (a != m_author) { m_author = a; emit authorChanged(); } } QString author() const { return m_author; } signals: void authorChanged(); private: QString m_author; };
Se puede pasar una instancia de Message como propiedad requerida a un archivo llamado MyItem.qml para que esté disponible:
int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); QQuickView view; Message msg; view.setInitialProperties({{"msg", &msg}}); view.setSource(QUrl::fromLocalFile("MyItem.qml")); view.show(); return app.exec(); }
Entonces, la propiedad author podría ser leída desde MyItem.qml:
// MyItem.qml import QtQuick Text { required property Message msg width: 100; height: 100 text: msg.author // invokes Message::author() to get this value Component.onCompleted: { msg.author = "Jonah" // invokes Message::setAuthor() } }
Para obtener la máxima interoperabilidad con QML, cualquier propiedad que se pueda escribir debe tener una señal NOTIFY asociada que se emita siempre que el valor de la propiedad haya cambiado. Esto permite que la propiedad se utilice con la vinculación de propiedades, que es una característica esencial de QML que refuerza las relaciones entre las propiedades mediante la actualización automática de una propiedad cada vez que cualquiera de sus dependencias cambia de valor.
En el ejemplo anterior, la señal NOTIFY asociada para la propiedad author es authorChanged, como se especifica en la macro Q_PROPERTY(). Esto significa que cada vez que se emite la señal - como ocurre cuando cambia el autor en Message::setAuthor() - se notifica al motor QML que debe actualizarse cualquier enlace que implique la propiedad author y, a su vez, el motor actualizará la propiedad text llamando de nuevo a Message::author().
Si la propiedad author fuera escribible pero no tuviera una señal NOTIFY asociada, el valor text se inicializaría con el valor inicial devuelto por Message::author() pero no se actualizaría con ningún cambio posterior de esta propiedad. Además, cualquier intento de enlazar con la propiedad desde QML producirá una advertencia de ejecución del motor.
Nota: se recomienda que la señal NOTIFY se denomine <propiedad>Cambiada, donde <property> es el nombre de la propiedad. El controlador de la señal de cambio de propiedad asociada generado por el motor QML siempre tendrá la forma on<Property>Changed, independientemente del nombre de la señal C++ relacionada, por lo que se recomienda que el nombre de la señal siga esta convención para evitar cualquier confusión.
Notas sobre el uso de las señales de notificación
Para evitar bucles o evaluaciones excesivas, los desarrolladores deben asegurarse de que la señal de cambio de propiedad sólo se emite cuando el valor de la propiedad ha cambiado realmente. Además, si una propiedad o grupo de propiedades se utiliza con poca frecuencia, está permitido utilizar la misma señal NOTIFY para varias propiedades. Esto debe hacerse con cuidado para garantizar que el rendimiento no se resienta.
La presencia de una señal NOTIFY conlleva una pequeña sobrecarga. Hay casos en los que el valor de una propiedad se establece en el momento de la construcción del objeto y no cambia posteriormente. El caso más común es cuando un tipo utiliza propiedades agrupadas y el objeto de propiedad agrupada se asigna una vez y sólo se libera cuando se elimina el objeto. En estos casos, se puede añadir el atributo CONSTANT a la declaración de la propiedad en lugar de una señal NOTIFY.
El atributo CONSTANT sólo debe utilizarse para propiedades cuyo valor se establece, y finaliza, únicamente en el constructor de la clase. Todas las demás propiedades que deseen utilizarse en enlaces deben tener una señal NOTIFY en su lugar.
Propiedades con tipos de objeto
Las propiedades de tipo objeto son accesibles desde QML siempre que el tipo de objeto se haya registrado adecuadamente en el sistema de tipos de QML.
Por ejemplo, el tipo Message puede tener una propiedad body del tipo MessageBody*:
class Message : public QObject { Q_OBJECT Q_PROPERTY(MessageBody* body READ body WRITE setBody NOTIFY bodyChanged) public: MessageBody* body() const; void setBody(MessageBody* body); }; class MessageBody : public QObject { Q_OBJECT Q_PROPERTY(QString text READ text WRITE text NOTIFY textChanged) // ... }
Supongamos que el tipo Message se ha registrado en el sistema de tipos QML, lo que permite utilizarlo como tipo de objeto desde el código QML:
Message { // ... }
Si el tipo MessageBody también estuviera registrado en el sistema de tipos, sería posible asignar MessageBody a la propiedad body de un Message, todo ello desde el código QML:
Message { body: MessageBody { text: "Hello, world!" } }
Propiedades con tipos de lista de objetos
Las propiedades que contienen listas de tipos derivados de QObject también pueden exponerse a QML. Sin embargo, para ello debe utilizarse QQmlListProperty en lugar de QList<T> como tipo de propiedad. Esto se debe a que QList no es un tipo derivado de QObject, por lo que no puede proporcionar las características de propiedad QML necesarias a través del sistema de objetos meta de Qt, como las notificaciones de señalización cuando se modifica una lista.
Por ejemplo, la clase MessageBoard a continuación tiene una propiedad messages de tipo QQmlListProperty que almacena una lista de instancias Message:
class MessageBoard : public QObject { Q_OBJECT Q_PROPERTY(QQmlListProperty<Message> messages READ messages) public: QQmlListProperty<Message> messages(); private: static void append_message(QQmlListProperty<Message> *list, Message *msg); QList<Message *> m_messages; };
La función MessageBoard::messages() simplemente crea y devuelve un QQmlListProperty a partir de su miembro QList<T> m_messages, pasando las funciones de modificación de lista apropiadas según lo requiera el constructor QQmlListProperty:
QQmlListProperty<Message> MessageBoard::messages() { return QQmlListProperty<Message>(this, 0, &MessageBoard::append_message); } void MessageBoard::append_message(QQmlListProperty<Message> *list, Message *msg) { MessageBoard *msgBoard = qobject_cast<MessageBoard *>(list->object); if (msg) msgBoard->m_messages.append(msg); }
Tenga en cuenta que el tipo de clase de plantilla para el QQmlListProperty - en este caso, Message - debe estar registrado en el sistema de tipos QML.
Propiedades agrupadas
Cualquier propiedad de tipo objeto de sólo lectura es accesible desde el código QML como propiedad agrupada. Esto puede utilizarse para exponer un grupo de propiedades relacionadas que describen un conjunto de atributos para un tipo.
Por ejemplo, supongamos que la propiedad Message::author fuera del tipo MessageAuthor en lugar de una simple cadena, con subpropiedades de name y email:
class MessageAuthor : public QObject { Q_PROPERTY(QString name READ name WRITE setName) Q_PROPERTY(QString email READ email WRITE setEmail) public: ... }; class Message : public QObject { Q_OBJECT Q_PROPERTY(MessageAuthor* author READ author) public: Message(QObject *parent) : QObject(parent), m_author(new MessageAuthor(this)) { } MessageAuthor *author() const { return m_author; } private: MessageAuthor *m_author; };
La propiedad author podría escribirse utilizando la sintaxis de propiedades agrupadas en QML, de esta forma:
Message { author.name: "Alexandra" author.email: "alexandra@mail.com" }
Un tipo que se expone como propiedad agrupada difiere de una propiedad de tipo objeto en que la propiedad agrupada es de sólo lectura, y es inicializada a un valor válido por el objeto padre en la construcción. Las subpropiedades de la propiedad agrupada pueden modificarse desde QML, pero el objeto de la propiedad agrupada en sí nunca cambiará, mientras que a una propiedad de tipo objeto se le puede asignar un nuevo valor de objeto desde QML en cualquier momento. Así, el tiempo de vida de un objeto de propiedad agrupada está controlado estrictamente por la implementación C++ padre, mientras que una propiedad de tipo objeto puede crearse y destruirse libremente mediante código QML.
Exposición de métodos (incluidas las ranuras de Qt)
Cualquier método de un tipo derivado de QObject es accesible desde código QML si lo es:
- Un método público marcado con la macro Q_INVOKABLE()
- Un método que sea un slot Qt público
Por ejemplo, la clase MessageBoard a continuación tiene un método postMessage() que ha sido marcado con la macro Q_INVOKABLE, así como un método refresh() que es un slot público:
clase MessageBoard : public QObject { Q_OBJECT QML_ELEMENTpublic: Q_INVOKABLE bool postMessage(const QString &msg) { qDebug() << "Called the C++ method with" << msg; return true; }public slots: void refresh() { qDebug() << "Called the C++ slot"; } };
Si se estableciera una instancia de MessageBoard como la propiedad requerida para un archivo MyItem.qml, entonces MyItem.qml podría invocar los dos métodos como se muestra en los siguientes ejemplos:
| C++ | int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); MessageBoard msgBoard; QQuickView view; view.setInitialProperties({{"msgBoard", &msgBoard}}); view.setSource(QUrl::fromLocalFile("MyItem.qml")); view.show(); return app.exec(); } |
| QML |
Si un método C++ tiene un parámetro con un tipo QObject*, el valor del parámetro se puede pasar desde QML utilizando un objeto id o un valor JavaScript var que haga referencia al objeto.
QML admite la llamada a funciones C++ sobrecargadas. Si hay varias funciones C++ con el mismo nombre pero argumentos diferentes, se llamará a la función correcta según el número y los tipos de argumentos que se proporcionen.
Los valores devueltos por métodos C++ se convierten en valores JavaScript cuando se accede a ellos desde expresiones JavaScript en QML.
Métodos C++ y el objeto "this
Es posible que desee recuperar un método C++ de un objeto e invocarlo en un objeto diferente. Considere el siguiente ejemplo, dentro de un módulo QML llamado Example:
| C++ | |
| QML | import QtQml import Example Invokable { objectName: "parent" property Invokable child: Invokable {} Component.onCompleted: child.invoke.call(this) } |
Si cargas el código QML desde un main.cpp adecuado, debería imprimir "invoked on parent". Sin embargo, debido a un error de larga data, no lo hace. Históricamente, el objeto 'this' de los métodos basados en C++ está inseparablemente unido al método. Cambiar este comportamiento para el código existente causaría errores sutiles ya que el objeto 'this' está implícito en muchos lugares. Desde Qt 6.5 puedes optar explícitamente por el comportamiento correcto y permitir que los métodos C++ acepten un objeto 'this'. Para hacerlo, añade el siguiente pragma a tus documentos QML:
pragma NativeMethodBehavior: AcceptThisObject
Con esta línea añadida, el ejemplo anterior funcionará como se espera.
Sobreescritura de toString()
Si proporciona un método Q_INVOKABLE llamado toString (sin argumentos), ese método se utilizará para convertir el objeto en una cadena en lugar de la implementación nativa toString de JavaScript.
Exposición de señales
Cualquier señal pública de un tipo derivado de QObject es accesible desde el código QML.
El motor QML crea automáticamente un controlador de señales para cualquier señal de un tipo derivado de QObject que se utilice desde QML. Los manejadores de señales siempre se denominan on<Signal>, donde <Signal> es el nombre de la señal, con la primera letra en mayúscula. Todos los parámetros pasados por la señal están disponibles en el manejador de señales a través de los nombres de los parámetros.
Por ejemplo, supongamos que la clase MessageBoard tiene una señal newMessagePosted() con un único parámetro, subject:
class MessageBoard : public QObject { Q_OBJECT public: // ... signals: void newMessagePosted(const QString &subject); };
Si el tipo MessageBoard estuviera registrado en el sistema de tipos QML, entonces un objeto MessageBoard declarado en QML podría recibir la señal newMessagePosted() utilizando un manejador de señales llamado onNewMessagePosted, y examinar el valor del parámetro subject:
MessageBoard { onNewMessagePosted: (subject)=> console.log("New message received:", subject) }
Al igual que ocurre con los valores de propiedad y los parámetros de método, un parámetro de señal debe tener un tipo admitido por el motor QML; consulte Conversión de tipos de datos entre QML y C++. (El uso de un tipo no registrado no generará un error, pero el valor del parámetro no será accesible desde el manejador).
Las clases pueden tener varias señales con el mismo nombre, pero sólo la señal final es accesible como señal QML. Tenga en cuenta que las señales con el mismo nombre pero diferentes parámetros no pueden distinguirse entre sí.
Véase también QML Type Registration Macros y Definición de tipos QML desde C++.
© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.