Definición de tipos QML desde C
Al ampliar QML con código C++, se puede registrar una clase C++ en el sistema de tipos de QML para que la clase se pueda utilizar como tipo de datos dentro del código QML. Aunque las propiedades, métodos y señales de cualquier clase derivada de QObject son accesibles desde QML, como se explica en Exposición de atributos de tipos C++ a QML, dicha clase no puede utilizarse como tipo de datos desde QML hasta que se registre en el sistema de tipos. Además, el registro puede proporcionar otras características, como permitir que una clase se utilice como un tipo de objeto QML instanciable desde QML, o permitir que una instancia singleton de la clase se importe y utilice desde QML.
Además, el módulo Qt Qml proporciona mecanismos para implementar características específicas de QML, como propiedades adjuntas y propiedades predeterminadas en C++.
(Tenga en cuenta que varios de los conceptos importantes tratados en este documento se demuestran en el tutorial Escribir extensiones QML con C++ ).
NOTA: Todas las cabeceras que declaran tipos QML deben ser accesibles sin ningún prefijo desde la ruta de inclusión del proyecto.
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.
Registro de tipos C++ en el sistema de tipos QML
Una clase derivada de QObject puede registrarse en el sistema de tipos de QML para que el tipo pueda utilizarse como tipo de datos desde el código QML.
El motor permite registrar tipos instanciables y no instanciables. El registro de un tipo instanciable permite utilizar una clase C++ como definición de un tipo de objeto QML, lo que permite utilizarla en declaraciones de objetos desde el código QML para crear objetos de este tipo. El registro también proporciona al motor metadatos de tipo adicionales, lo que permite utilizar el tipo (y cualquier enum declarado por la clase) como tipo de datos para valores de propiedades, parámetros de métodos y valores de retorno, y parámetros de señales que se intercambian entre QML y C++.
El registro de un tipo no sustancial también registra la clase como un tipo de datos de esta forma, pero el tipo no se puede utilizar instanciado como un tipo de objeto QML desde QML. Esto es útil, por ejemplo, si un tipo tiene enums que deben exponerse a QML pero el tipo en sí no debe ser instanciable.
Para obtener una guía rápida sobre cómo elegir el enfoque correcto para exponer tipos C++ a QML, consulte Elección del método de integración correcto entre C++ y QML.
Precondiciones
Todas las macros mencionadas a continuación están disponibles en el archivo de cabecera qqmlintegration.h del módulo QtQmlIntegration.
Es necesario añadir el siguiente código a los archivos que las utilizan para que las macros estén disponibles:
#include <QtQmlIntegration/qqmlintegration.h>Si ya está enlazando con el módulo QtQml, puede utilizar en su lugar el archivo de cabecera qqmlregistration.h, que incluirá qqmlintegration.h, como se indica a continuación:
#include <QtQml/qqmlregistration.h>Además, las declaraciones de clase deben estar en cabeceras accesibles a través de la ruta de inclusión del proyecto. Las declaraciones se utilizan para generar código de registro en tiempo de compilación, y el código de registro necesita incluir las cabeceras que contienen las declaraciones.
Registro de un tipo de objeto instanciable
Cualquier clase C++ derivada de QObject puede registrarse como definición de un tipo de objeto QML. Una vez registrada una clase en el sistema de tipos de QML, puede declararse e instanciarse como cualquier otro tipo de objeto desde el código QML. Una vez creada, la instancia de una clase puede manipularse desde QML; como explica Exposing Attributes of C++ Types to QML, las propiedades, métodos y señales de cualquier clase derivada de QObject son accesibles desde el código QML.
Para registrar una clase derivada de QObject como un tipo de objeto QML instanciable, añada QML_ELEMENT o QML_NAMED_ELEMENT(<name>) a la declaración de la clase. También es necesario realizar ajustes en el sistema de compilación. Para qmake, añada CONFIG += qmltypes, un QML_IMPORT_NAME, y un QML_IMPORT_MAJOR_VERSION al archivo del proyecto. Para CMake, el archivo que contiene la clase debe ser parte de una configuración de destino con qt_add_qml_module(). Esto registrará la clase en el espacio de nombres de tipo bajo la versión principal dada, utilizando el nombre de la clase o un nombre dado explícitamente como nombre de tipo QML. La(s) versión(es) menor(es) se derivará(n) de cualquier revisión adjunta a propiedades, métodos o señales. La versión menor por defecto es 0. Puede restringir explícitamente el tipo para que sólo esté disponible a partir de versiones menores específicas añadiendo la macro QML_ADDED_IN_VERSION() a la declaración de la clase. Los clientes pueden importar versiones adecuadas del espacio de nombres para utilizar el tipo.
Por ejemplo, supongamos que existe una clase Message con propiedades author y creationDate:
class Message : public QObject { Q_OBJECT Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged) Q_PROPERTY(QDateTime creationDate READ creationDate WRITE setCreationDate NOTIFY creationDateChanged) QML_ELEMENT public: // ... };
Este tipo puede registrarse añadiendo un espacio de nombres de tipo y un número de versión adecuados al archivo de proyecto. Por ejemplo, para que el tipo esté disponible en el espacio de nombres com.mycompany.messaging con la versión 1.0:
qt_add_qml_module(messaging
URI com.mycompany.messaging
VERSION 1.0
SOURCES
message.cpp message.h
)CONFIG += qmltypes QML_IMPORT_NAME = com.mycompany.messaging QML_IMPORT_MAJOR_VERSION = 1
Si la cabecera en la que se declara la clase no es accesible desde la ruta de inclusión de su proyecto, es posible que tenga que modificar la ruta de inclusión para que se pueda compilar el código de registro generado.
INCLUDEPATH += com/mycompany/messaging
La clase se puede utilizar en una declaración de objeto desde QML, y se puede leer y escribir en sus propiedades, según el ejemplo siguiente:
import com.mycompany.messaging Message { author: "Amelie" creationDate: new Date() }
Registro de tipos de valor
Cualquier tipo con una macro Q_GADGET puede registrarse como tipo de valor QML. Una vez registrado un tipo de este tipo en el sistema de tipos de QML, puede utilizarse como tipo de propiedad en el código QML. Una instancia de este tipo puede manipularse desde QML; como explica Exposing Attributes of C++ Types to QML, las propiedades y métodos de cualquier tipo de valor son accesibles desde el código QML.
A diferencia de los tipos objeto, los tipos valor requieren nombres en minúsculas. La forma preferida de registrarlos es utilizar las macros QML_VALUE_TYPE o QML_ANONYMOUS. No existe un equivalente a QML_ELEMENT, ya que las clases C++ suelen tener nombres en mayúsculas. Por lo demás, el registro es muy similar al de los tipos objeto.
Por ejemplo, supongamos que quieres registrar un tipo de valor person que consiste en dos cadenas para nombre y apellidos:
class Person { Q_GADGET Q_PROPERTY(QString firstName READ firstName WRITE setFirstName) Q_PROPERTY(QString lastName READ lastName WRITE setLastName) QML_VALUE_TYPE(person) public: // ... };
Existen algunas limitaciones adicionales sobre lo que se puede hacer con los tipos de valor:
- Los tipos de valor no pueden ser singletons.
- Los tipos de valor deben poder construirse por defecto y copiarse.
- Utilizar QProperty como miembro de un tipo de valor es problemático. Los tipos de valor se copian, y tendrías que decidir qué hacer con cualquier enlace en QProperty en ese momento. No debe utilizar QProperty en tipos de valor.
- Los tipos de valor no pueden proporcionar propiedades adjuntas.
- La API para definir extensiones de tipos de valor (QML_EXTENDED) no es pública y está sujeta a futuros cambios.
Tipos de valor con enumeraciones
La exposición de enumeraciones de un tipo de valor a QML requiere algunos pasos adicionales.
Los tipos de valor tienen nombres en minúsculas en QML y los tipos con nombres en minúsculas no suelen ser direccionables en código JavaScript (a menos que se especifique pragma ValueTypeBehavior: Addressable). Si tiene un tipo de valor en C++ con una enumeración que desea exponer a QML, debe exponer la enumeración por separado.
Esto puede resolverse utilizando QML_FOREIGN_NAMESPACE. En primer lugar, derive de su tipo de valor para crear un tipo C++ independiente:
class Person { Q_GADGET Q_PROPERTY(QString firstName READ firstName WRITE setFirstName) Q_PROPERTY(QString lastName READ lastName WRITE setLastName) QML_VALUE_TYPE(person) public: enum TheEnum { A, B, C }; Q_ENUM(TheEnum) //... }; class PersonDerived: public Person { Q_GADGET };
A continuación, exponer el tipo derivado como un espacio de nombres extranjero:
namespace PersonDerivedForeign
{
Q_NAMESPACE
QML_NAMED_ELEMENT(Person)
QML_FOREIGN_NAMESPACE(PersonDerived)
}Esto produce un espacio de nombres QML llamado Person (mayúsculas) con una enumeración llamada TheEnum y valores A, B, y C. Entonces puedes escribir lo siguiente en QML:
someProperty: Person.A
Al mismo tiempo puede seguir utilizando su tipo de valor llamado person (minúsculas) exactamente igual que antes.
Registro de tipos no sustanciales
A veces puede ser necesario registrar una clase derivada de QObject en el sistema de tipos de QML, pero no como un tipo instanciable. Por ejemplo, éste es el caso de una clase C++:
- es un tipo de interfaz que no debe ser instanciable
- es un tipo de clase base que no necesita exponerse a QML
- declara un enum que debe ser accesible desde QML, pero que no debe ser instanciable
- es un tipo que debe proporcionarse a QML a través de una instancia singleton y que no debe ser instanciable desde QML
El módulo Qt Qml proporciona varias macros para registrar tipos no instanciables:
- QML_ANONYMOUS registra un tipo C++ que no es instanciable y al que no se puede hacer referencia desde QML. Esto permite al motor coaccionar cualquier tipo heredado que sea instanciable desde QML.
- QML_INTERFACE registra un tipo de interfaz Qt existente. El tipo no es instanciable desde QML y no se pueden declarar propiedades QML con él. Sin embargo, el uso de propiedades C++ de este tipo desde QML realizará los cambios de interfaz esperados.
- QML_UNCREATABLE(reason) combinado con QML_ELEMENT o QML_NAMED_ELEMENT registra un tipo C++ con nombre que no es instanciable pero que debería ser identificable como un tipo para el sistema de tipos QML. Esto es útil si los enums de un tipo o las propiedades adjuntas deben ser accesibles desde QML pero el tipo en sí no debe ser instanciable. El parámetro debe ser un mensaje de error que se emitirá si se detecta un intento de crear una instancia del tipo.
- QML_SINGLETON combinado con QML_ELEMENT o QML_NAMED_ELEMENT registra un tipo singleton que puede importarse desde QML, como se explica a continuación.
Tenga en cuenta que todos los tipos C++ registrados en el sistema de tipos QML deben ser derivados de QObject, incluso si no son instantáneos.
Registro de objetos Singleton con un tipo Singleton
Un tipo singleton permite exponer propiedades, señales y métodos en un espacio de nombres sin necesidad de que el cliente instancie manualmente una instancia de objeto. Los tipos singleton de QObject, en particular, son una forma eficaz y cómoda de proporcionar funcionalidad o valores de propiedad globales.
Tenga en cuenta que los tipos singleton no tienen un QQmlContext asociado, ya que se comparten en todos los contextos de un motor. QObject Las instancias de tipo singleton se construyen y son propiedad del QQmlEngine, y se destruirán cuando se destruya el motor.
Se puede interactuar con un tipo QObject singleton de forma similar a cualquier otro tipo QObject o instanciado, excepto que sólo existirá una instancia (construida y propiedad del motor), y se debe hacer referencia a ella por el nombre del tipo en lugar del id. Los Q_PROPERTYs de los tipos singleton de QObject pueden ser enlazados, y las funciones de Q_INVOKABLE de las APIs de los módulos de QObject pueden ser utilizadas en las expresiones de los manejadores de señales. Esto hace que los tipos singleton sean una forma ideal de implementar estilos o tematización, y también pueden utilizarse en lugar de importaciones de scripts ".pragma library" para almacenar estado global o proporcionar funcionalidad global.
Una vez registrado, un tipo singleton QObject puede importarse y utilizarse como cualquier otra instancia QObject expuesta a QML. El siguiente ejemplo supone que se ha registrado un tipo singleton QObject en el espacio de nombres "MyThemeModule" con la versión 1.0, donde ese QObject tiene un QColor "color" Q_PROPERTY:
import MyThemeModule 1.0 as Theme Rectangle { color: Theme.color // binding. }
Un QJSValue también puede exponerse como tipo singleton, pero los clientes deben tener en cuenta que las propiedades de un tipo singleton de este tipo no pueden vincularse.
Consulte QML_SINGLETON para obtener más información sobre cómo implementar y registrar un nuevo tipo singleton, y cómo utilizar un tipo singleton existente. Consulte Singletons in QML para obtener información más detallada sobre los singletons.
Nota: Los valores Enum de los tipos registrados en QML deben empezar por mayúscula.
Propiedades finales
Las propiedades declaradas como finales utilizando el modificador FINAL en Q_PROPERTY no pueden sobrescribirse. Esto significa que cualquier propiedad o función con el mismo nombre, declarada en QML o en C++ sobre tipos derivados, es ignorada por el motor QML. Debe declarar las propiedades FINAL siempre que sea posible, para evitar anulaciones accidentales. Una anulación de una propiedad es visible no sólo en las clases derivadas, sino también para el código QML que ejecuta el contexto de la clase base. Sin embargo, este código QML suele esperar la propiedad original. Esta es una fuente frecuente de errores.
Las propiedades declaradas FINAL tampoco pueden ser invocadas por funciones en QML, o por métodos Q_INVOKABLE en C++.
Revisiones y versiones de tipos
Muchas de las funciones de registro de tipos requieren que se especifiquen versiones para el tipo registrado. Las revisiones y versiones de tipos permiten que existan nuevas propiedades o métodos en la nueva versión sin dejar de ser compatibles con las versiones anteriores.
Considere estos dos archivos QML:
// main.qml import QtQuick 1.0 Item { id: root MyType {} }
// MyType.qml import MyTypes 1.0 CppType { value: root.x }
donde CppType corresponde a la clase C++ CppType.
Si el autor de CppType añade una propiedad root a CppType en una nueva versión de su definición de tipo, root.x ahora se resuelve a un valor diferente porque root es también el id del componente de nivel superior. El autor podría especificar que la nueva propiedad root esté disponible a partir de una versión menor específica. Esto permite añadir nuevas propiedades y características a los tipos existentes sin romper los programas existentes.
La etiqueta REVISION se utiliza para marcar la propiedad root como añadida en la revisión 1 del tipo. Los métodos como Q_INVOKABLE's, señales y ranuras también pueden etiquetarse para una revisión utilizando la macro Q_REVISION:
class CppType : public BaseType { Q_OBJECT Q_PROPERTY(int root READ root WRITE setRoot NOTIFY rootChanged REVISION(1, 0)) QML_ELEMENT signals: Q_REVISION(1, 0) void rootChanged(); };
Las revisiones así indicadas se interpretan automáticamente como versiones menores de la versión mayor indicada en el archivo de proyecto. En este caso, root sólo está disponible cuando se importa MyTypes versión 1.1 o superior. Las importaciones de MyTypes versión 1.0 no se ven afectadas.
Por la misma razón, los nuevos tipos introducidos en versiones posteriores deben etiquetarse con la macro QML_ADDED_IN_VERSION.
Esta característica del lenguaje permite realizar cambios de comportamiento sin romper las aplicaciones existentes. Por consiguiente, los autores de módulos QML deben recordar siempre documentar los cambios entre versiones menores, y los usuarios de módulos QML deben comprobar que su aplicación sigue funcionando correctamente antes de desplegar una sentencia import actualizada.
Las revisiones de una clase base de la que depende su tipo se registran automáticamente al registrar el propio tipo. Esto es útil cuando se deriva de clases base proporcionadas por otros autores, por ejemplo, cuando se extienden clases del módulo Qt Quick.
Nota: El motor QML no admite revisiones para propiedades o señales de objetos de propiedades agrupadas y adjuntas.
Registro de objetos de extensión
Al integrar clases y tecnología existentes en QML, a menudo será necesario modificar las API para que se adapten mejor al entorno declarativo. Aunque los mejores resultados suelen obtenerse modificando directamente las clases originales, si esto no es posible o se complica por otros motivos, los objetos de extensión permiten posibilidades de extensión limitadas a tipos que se controlan sin modificaciones directas. La extensión de tipos propios de Qt no está soportada.
Losobjetos de extensión añaden propiedades adicionales a un tipo existente. Una definición de tipo extendido permite al programador proporcionar un tipo adicional, conocido como tipo de extensión, al registrar la clase. Sus miembros se fusionan de forma transparente con la clase de destino original cuando se utiliza desde dentro de QML. Por ejemplo:
QLineEdit { leftMargin: 20 }
La propiedad leftMargin es una nueva propiedad añadida a un tipo C++ existente, QLineEdit, sin modificar su código fuente.
La macro QML_EXTENDED(extensión) sirve para registrar tipos extendidos. El argumento es el nombre de otra clase que se utilizará como extensión.
También puede utilizar QML_EXTENDED_NAMESPACE(namespace) para registrar un espacio de nombres, y especialmente las enumeraciones declaradas en él, como extensión de un tipo. Si el tipo que está extendiendo es en sí mismo un espacio de nombres, debe utilizar QML_NAMESPACE_EXTENDED(namespace) en su lugar.
Una clase de extensión es un QObject normal, con un constructor que toma un puntero QObject. Sin embargo, la creación de la clase de extensión se retrasa hasta que se accede a la primera propiedad extendida. La clase de extensión se crea y el objeto de destino se pasa como padre. Cuando se accede a la propiedad del original, se utiliza en su lugar la propiedad correspondiente del objeto de extensión.
Registro de tipos externos
Puede haber tipos C++ que no puedan ser modificados para contener las macros mencionadas anteriormente. Pueden ser tipos de librerías de terceros, o tipos que necesitan cumplir algún contrato que contradice la presencia de esas macros. Sin embargo, puedes exponer esos tipos a QML utilizando la macro QML_FOREIGN. Para ello, cree una estructura separada que consista enteramente en las macros de registro, como esta:
// Contains class Immutable3rdParty #include <3rdpartyheader.h> struct Foreign { Q_GADGET QML_FOREIGN(Immutable3rdParty) QML_NAMED_ELEMENT(Accessible3rdParty) QML_ADDED_IN_VERSION(2, 4) // QML_EXTENDED, QML_SINGLETON ... };
De este código, se obtiene un tipo QML con los métodos y propiedades de Immutable3rdParty, y los rasgos QML (por ejemplo: singleton, extended) especificados en Foreign.
Definición de tipos y atributos específicos de QML
Proporcionar propiedades adjuntas
En la sintaxis del lenguaje QML existe la noción de propiedades adjuntas y manejadores de señales adjuntos, que son atributos adicionales que se adjuntan a un objeto. Básicamente, estos atributos son implementados y proporcionados por un tipo de adjunto, y estos atributos pueden adjuntarse a un objeto de otro tipo. Esto contrasta con las propiedades ordinarias de los objetos, que son proporcionadas por el propio tipo del objeto (o por el tipo heredado del objeto).
Por ejemplo, la siguiente dirección Item utiliza propiedades adjuntas y manejadores adjuntos:
import QtQuick 2.0 Item { width: 100; height: 100 focus: true Keys.enabled: false Keys.onReturnPressed: console.log("Return key was pressed") }
Aquí, el objeto Item es capaz de acceder y establecer los valores de Keys.enabled y Keys.onReturnPressed. Esto permite al objeto Item acceder a estos atributos adicionales como una extensión de sus propios atributos existentes.
Pasos para implementar objetos adjuntos
Al considerar el ejemplo anterior, hay varias partes implicadas:
- Hay una instancia de un tipo de objeto adjunto anónimo, con una propiedad
enabledy una señalreturnPressed, que se ha adjuntado al objeto Item para permitirle acceder y establecer estos atributos. - El objeto Item es el attachee, al que se ha adjuntado la instancia del tipo de objeto attached.
- Keys es el tipo de adjunto, que proporciona al adjunto un calificador con nombre, "Keys", a través del cual puede acceder a los atributos del tipo de objeto adjunto.
Cuando el motor QML procesa este código, crea una única instancia del tipo de objeto adjunto y adjunta esta instancia al objeto Item, proporcionándole así acceso a los atributos enabled y returnPressed de la instancia.
Los mecanismos para proporcionar objetos adjuntos pueden implementarse desde C++ proporcionando clases para el tipo de objeto ad junto y el tipo de adjunto. Para el tipo de objeto adjunto, proporcione una clase derivada de QObject que defina los atributos accesibles a los objetos adjuntos. Para el tipo de objeto adjunto, proporcione una clase derivada de QObject que:
- implemente una función estática qmlAttachedProperties() con la siguiente firma:
static <AttachedPropertiesType> *qmlAttachedProperties(QObject *object);
Este método debe devolver una instancia del tipo de objeto adjunto.
El motor QML invoca este método para adjuntar una instancia del tipo de objeto attached al attachee especificado por el parámetro
object. Es habitual, aunque no estrictamente necesario, que la implementación de este método emparente la instancia devuelta conobjectpara evitar fugas de memoria.El motor llama a este método como máximo una vez por cada instancia de objeto adjunto, ya que almacena en caché el puntero de la instancia devuelta para posteriores accesos a propiedades adjuntas. En consecuencia, el objeto adjunto no se puede eliminar hasta que se destruya el adjunto
object. - se declara como tipo de adjunto, añadiendo la macro QML_ATTACHED(attached) a la declaración de la clase. El argumento es el nombre del tipo de objeto adjunto
Implementación de objetos adjuntos: Un ejemplo
Por ejemplo, tomemos el tipo Message descrito en un ejemplo anterior:
class Message : public QObject { Q_OBJECT Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged) Q_PROPERTY(QDateTime creationDate READ creationDate WRITE setCreationDate NOTIFY creationDateChanged) QML_ELEMENT public: // ... };
Supongamos que es necesario activar una señal en un Message cuando se publica en un tablón de anuncios, y también realizar un seguimiento de cuándo ha caducado el mensaje en el tablón de anuncios. Dado que no tiene sentido añadir estos atributos directamente a Message, ya que los atributos son más relevantes para el contexto del tablón de anuncios, podrían implementarse como atributos adjuntos en un objeto Message que se proporcionan a través de un calificador "Tablón de anuncios". En términos de los conceptos descritos anteriormente, las partes implicadas aquí son:
- Una instancia de un tipo de objeto adjunto anónimo, que proporciona una señal
publishedy una propiedadexpired. Este tipo se implementa medianteMessageBoardAttachedTypea continuación - Un objeto
Message, que será el adjunto - El tipo
MessageBoard, que será el tipo adjunto que utilizarán los objetosMessagepara acceder a los atributos adjuntos
A continuación se muestra un ejemplo de implementación. En primer lugar, es necesario que exista un tipo de objeto adjunto con las propiedades y señales necesarias que serán accesibles al attachee:
class MessageBoardAttachedType : public QObject { Q_OBJECT Q_PROPERTY(bool expired READ expired WRITE setExpired NOTIFY expiredChanged) QML_ANONYMOUS public: MessageBoardAttachedType(QObject *parent); bool expired() const; void setExpired(bool expired); signals: void published(); void expiredChanged(); };
A continuación, el tipo adjunto, MessageBoard, debe declarar un método qmlAttachedProperties() que devuelva una instancia del tipo de objeto ad junto tal y como se implementa en MessageBoardAttachedType. Además, MessageBoard debe declararse como un tipo adjunto a través de la macro QML_ATTACHED():
class MessageBoard : public QObject { Q_OBJECT QML_ATTACHED(MessageBoardAttachedType) QML_ELEMENT public: static MessageBoardAttachedType *qmlAttachedProperties(QObject *object) { return new MessageBoardAttachedType(object); } };
Ahora, un tipo Message puede acceder a las propiedades y señales del tipo de objeto adjunto:
Message { author: "Amelie" creationDate: new Date() MessageBoard.expired: creationDate < new Date("January 01, 2015 10:45:00") MessageBoard.onPublished: console.log("Message by", author, "has been published!") }
Además, la implementación de C++ puede acceder a la instancia de objeto adjunta que se ha adjuntado a cualquier objeto llamando a la función qmlAttachedPropertiesObject().
Por ejemplo:
Message *msg = someMessageInstance(); MessageBoardAttachedType *attached = qobject_cast<MessageBoardAttachedType*>(qmlAttachedPropertiesObject<MessageBoard>(msg)); qDebug() << "Value of MessageBoard.expired:" << attached->expired();
Propagación de propiedades adjuntas
QQuickAttachedPropertyPropagator puede subclasificarse para propagar propiedades adjuntas de un objeto padre a sus hijos, de forma similar a la propagación de font y palette. Soporta la propagación a través de items, popups, y windows.
Tipos de modificadores de propiedades
Un tipo modificador de propiedad es una clase especial de tipo de objeto QML. Una instancia de tipo modificador de propiedad afecta a una propiedad (de una instancia de objeto QML) a la que se aplica. Existen dos tipos diferentes de modificadores de propiedad:
- interceptores de escritura de valor de propiedad
- fuentes de valor de propiedad
Un interceptor de escritura de valores de propiedad puede utilizarse para filtrar o modificar valores a medida que se escriben en las propiedades. Actualmente, el único interceptor de escritura de valor de propiedad compatible es el tipo Behavior proporcionado por la importación QtQuick.
Se puede utilizar una fuente de valor de propiedad para actualizar automáticamente el valor de una propiedad a lo largo del tiempo. Los clientes pueden definir sus propios tipos de fuente de valor de propiedad. Los distintos tipos de animación de propiedades proporcionados por la importación QtQuick son ejemplos de fuentes de valor de propiedades.
Las instancias de tipo modificador de propiedad pueden crearse y aplicarse a una propiedad de un objeto QML mediante la sintaxis "<TipoModificador> on <nombreDePropiedad>", como muestra el siguiente ejemplo:
import QtQuick 2.0 Item { width: 400 height: 50 Rectangle { width: 50 height: 50 color: "red" NumberAnimation on x { from: 0 to: 350 loops: Animation.Infinite duration: 2000 } } }
Esto se conoce comúnmente como sintaxis "on".
Los clientes pueden registrar sus propios tipos de fuente de valor de propiedad, pero actualmente no los interceptores de escritura de valor de propiedad.
Fuentes de valor de propiedad
Lasfuentes de valor de propiedad son tipos QML que pueden actualizar automáticamente el valor de una propiedad a lo largo del tiempo, utilizando la sintaxis <PropertyValueSource> on <property>. Por ejemplo, los distintos tipos de animación de propiedades proporcionados por el módulo QtQuick son ejemplos de fuentes de valores de propiedades.
Una fuente de valor de propiedad puede implementarse en C++ subclasificando QQmlPropertyValueSource y proporcionando una implementación que escriba diferentes valores en una propiedad a lo largo del tiempo. Cuando la fuente de valor de propiedad se aplica a una propiedad utilizando la sintaxis <PropertyValueSource> on <property> en QML, el motor le da una referencia a esta propiedad para que el valor de la propiedad pueda actualizarse.
Por ejemplo, supongamos que hay una clase RandomNumberGenerator disponible como fuente de valor de propiedad, de modo que cuando se aplique a una propiedad QML, actualizará el valor de la propiedad a un número aleatorio diferente cada 500 milisegundos. Además, se puede proporcionar un maxValue a este generador de números aleatorios. Esta clase se puede implementar de la siguiente manera:
class RandomNumberGenerator : public QObject, public QQmlPropertyValueSource { Q_OBJECT Q_INTERFACES(QQmlPropertyValueSource) Q_PROPERTY(int maxValue READ maxValue WRITE setMaxValue NOTIFY maxValueChanged); QML_ELEMENT public: RandomNumberGenerator(QObject *parent) : QObject(parent), m_maxValue(100) { QObject::connect(&m_timer, SIGNAL(timeout()), SLOT(updateProperty())); m_timer.start(500); } int maxValue() const; void setMaxValue(int maxValue); virtual void setTarget(const QQmlProperty &prop) { m_targetProperty = prop; } signals: void maxValueChanged(); private slots: void updateProperty() { m_targetProperty.write(QRandomGenerator::global()->bounded(m_maxValue)); } private: QQmlProperty m_targetProperty; QTimer m_timer; int m_maxValue; };
Cuando el motor QML encuentra un uso de RandomNumberGenerator como fuente de valor de propiedad, invoca RandomNumberGenerator::setTarget() para proporcionar la clase con la propiedad a la que se ha aplicado la fuente de valor. Cuando el temporizador interno de RandomNumberGenerator se activa cada 500 milisegundos, escribirá un nuevo valor numérico en la propiedad especificada.
Una vez que la clase RandomNumberGenerator ha sido registrada en el sistema de tipos QML, puede ser utilizada desde QML como fuente de valores de propiedades. A continuación, se utiliza para cambiar el ancho de un Rectangle cada 500 milisegundos:
import QtQuick 2.0 Item { width: 300; height: 300 Rectangle { RandomNumberGenerator on width { maxValue: 300 } height: 100 color: "red" } }
En todos los demás aspectos, las fuentes de valores de propiedades son tipos QML normales que pueden tener propiedades, métodos de señales, etc., pero con la capacidad añadida de que pueden utilizarse para cambiar valores de propiedades utilizando la sintaxis <PropertyValueSource> on <property>.
Cuando se asigna un objeto fuente de valor de propiedad a una propiedad, QML primero intenta asignarlo normalmente, como si fuera un tipo QML normal. Sólo si esta asignación falla, el motor llama al método setTarget(). Esto permite utilizar el tipo en otros contextos además de como fuente de valores.
Especificación de las propiedades predeterminadas y principales de los tipos de objeto QML
Cualquier tipo derivado de QObject que se registre como tipo de objeto QML instanciable puede especificar opcionalmente una propiedad predeterminada para el tipo. Una propiedad por defecto es la propiedad a la que se asignan automáticamente los hijos de un objeto si no se asignan a ninguna propiedad específica.
La propiedad por defecto puede establecerse llamando a la macro Q_CLASSINFO() para una clase con un valor "DefaultProperty" específico. Por ejemplo, la clase MessageBoard a continuación especifica su propiedad messages como la propiedad por defecto para la clase:
class MessageBoard : public QObject { Q_OBJECT Q_PROPERTY(QQmlListProperty<Message> messages READ messages) Q_CLASSINFO("DefaultProperty", "messages") QML_ELEMENT public: QQmlListProperty<Message> messages(); private: QList<Message *> m_messages; };
Esto permite que los hijos de un objeto MessageBoard se asignen automáticamente a su propiedad messages si no están asignados a una propiedad específica. Por ejemplo:
MessageBoard { Message { author: "Naomi" } Message { author: "Clancy" } }
Si messages no se estableciera como propiedad por defecto, entonces cualquier objeto Message tendría que ser asignado explícitamente a la propiedad messages en su lugar, como sigue:
MessageBoard { messages: [ Message { author: "Naomi" }, Message { author: "Clancy" } ] }
(Por cierto, la propiedad Item::data es su propiedad por defecto. Cualquier objeto Item añadido a esta propiedad data se añade también a la lista de Item::children, por lo que el uso de la propiedad por defecto permite declarar hijos visuales para un elemento sin asignarlos explícitamente a la propiedad children ).
Además, puede declarar una "ParentProperty" Q_CLASSINFO() para informar al motor QML de qué propiedad debe denotar el objeto padre en la jerarquía QML. Por ejemplo, el tipo Message podría declararse de la siguiente manera:
class Message : public QObject { Q_OBJECT Q_PROPERTY(QObject* board READ board BINDABLE boardBindable) Q_PROPERTY(QString author READ author BINDABLE authorBindable) Q_CLASSINFO("ParentProperty", "board") QML_ELEMENT public: Message(QObject *parent = nullptr) : QObject(parent) { m_board = parent; } QObject *board() const { return m_board.value(); } QBindable<QObject *> boardBindable() { return QBindable<QObject *>(&m_board); } QString author() const { return m_author.value(); } QBindable<QString> authorBindable() { return QBindable<QString>(&m_author); } private: QProperty<QObject *> m_board; QProperty<QString> m_author; };
La definición de la propiedad padre permite a qmllint y a otras herramientas comprender mejor la intención del código y evita falsas advertencias positivas en algunos accesos a propiedades.
Definición de elementos visuales con el módulo Qt Quick
Al crear interfaces de usuario con el módulo Qt Quick todos los objetos QML que se vayan a representar visualmente deben derivar del tipo Item, ya que es el tipo base para todos los objetos visuales en el módulo Qt Quick. Este tipo Item se implementa mediante la clase C++ QQuickItem, que proporciona el módulo Qt Quick . Por lo tanto, esta clase debe subclasificarse cuando sea necesario implementar un tipo visual en C++ que pueda integrarse en una interfaz de usuario basada en QML.
Consulte la documentación de QQuickItem para obtener más información. Además, el tutorial Escribir extensiones QML con C++ demuestra cómo se puede implementar en C++ un elemento visual basado en QQuickItem e integrarlo en una interfaz de usuario basada en Qt Quick.
Recepción de notificaciones para la inicialización de objetos
Para algunos tipos de objetos QML personalizados, puede resultar beneficioso retrasar la inicialización de determinados datos hasta que se haya creado el objeto y se hayan establecido todas sus propiedades. Por ejemplo, este puede ser el caso si la inicialización es costosa, o si la inicialización no debe realizarse hasta que se hayan inicializado todos los valores de las propiedades.
El módulo Qt Qml proporciona la subclase QQmlParserStatus para estos fines. Define una serie de métodos virtuales que se invocan en varias etapas durante la instanciación del componente. Para recibir estas notificaciones, una clase C++ debe heredar QQmlParserStatus y notificarlo también al metasistema Qt mediante la macro Q_INTERFACES().
Por ejemplo:
class MyQmlType : public QObject, public QQmlParserStatus { Q_OBJECT Q_INTERFACES(QQmlParserStatus) QML_ELEMENT public: virtual void componentComplete() { // Perform some initialization here now that the object is fully created } };
Véase también QML Type Registration Macros y Visión general - Integración de QML y 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.