Propiedades enlazables de Qt
Qt proporciona propiedades enlazables. Las propiedades enlazables son propiedades que o bien tienen un valor o bien se especifican usando cualquier función C++, típicamente una expresión lambda C++. En el caso de que se especifiquen usando una función C++, se actualizan automáticamente cada vez que cambian sus dependencias.
Las propiedades vinculables se implementan en la clase QProperty, que consta del objeto de datos y un puntero a una estructura de datos de gestión, y en la clase QObjectBindableProperty, que consta únicamente del objeto de datos y utiliza el encapsulador QObject para almacenar el puntero a la estructura de datos de gestión.
¿Por qué utilizar propiedades enlazables?
Las propiedades enlazables son una de las características principales de QML. Permiten especificar relaciones entre diferentes propiedades de objetos y actualizar automáticamente los valores de las propiedades cuando cambian sus dependencias. Las propiedades enlazables permiten conseguir lo mismo no sólo en código QML, sino también en C++. El uso de propiedades enlazables puede ayudar a simplificar su programa, eliminando una gran cantidad de código repetitivo para el seguimiento y la reacción a las actualizaciones de dependencia de diferentes objetos.
El siguiente ejemplo introductorio muestra el uso de propiedades enlazables en código C++. También puede consultar el ejemplo de Propiedades enlazables para ver cómo las propiedades enlazables pueden ayudar a mejorar su código.
Ejemplo introductorio
La expresión vinculante calcula el valor leyendo otros valores de QProperty. Entre bastidores se realiza un seguimiento de esta dependencia. Cada vez que se detecta un cambio en la dependencia de cualquier propiedad, la expresión de vinculación se reevalúa y el nuevo resultado se aplica a la propiedad. Por ejemplo:
QProperty<QString> nombre("Juan");QProperty<QString> apellido("Smith");QProperty<int> age(41);QProperty<QString> fullname; fullname.setBinding([&]() { return firstname.value() + " " + lastname.value() + " age: " + QString::number(edad.valor()); }); qDebug() << fullname.value(); // Prints "John Smith age: 41" firstname = "Emma"; // Activa la reevaluación vinculante qDebug() << fullname.value(); // Prints the new value "Emma Smith age: 41" // Se acerca el cumpleañosage.setValue(age.value() + 1); // Activa la reevaluación qDebug() << fullname.value(); // Prints "Emma Smith age: 42"
Cuando se asigna un nuevo valor a la propiedad firstname, se reevalúa la expresión de enlace para fullname. Así, cuando la última sentencia qDebug() intenta leer el valor del nombre de la propiedad fullname, se devuelve el nuevo valor.
Dado que los bindings son funciones C++, pueden hacer cualquier cosa que sea posible en C++. Esto incluye llamar a otras funciones. Si esas funciones acceden a valores mantenidos por QProperty, se convierten automáticamente en dependencias del binding.
Las expresiones de vinculación pueden utilizar propiedades de cualquier tipo, por lo que en el ejemplo anterior la edad es un entero y se pliega en el valor de cadena utilizando la conversión a entero, pero la dependencia se rastrea completamente.
Obtención y establecimiento de propiedades vinculables
Cuando una clase tiene una propiedad vinculable, ya sea utilizando QProperty o QObjectBindableProperty, hay que tener especial cuidado al formular los getters y setters para esa propiedad.
Obtención de propiedades vinculables
Para garantizar el correcto funcionamiento del sistema automático de seguimiento de dependencias, todas las posibles rutas de código de un getter deben leerse desde el objeto de propiedad subyacente. Además, la propiedad no debe escribirse dentro del getter. Los patrones de diseño que recalculan o actualizan cualquier cosa en el getter no son compatibles con las propiedades vinculables.
Por lo tanto, se recomienda utilizar únicamente getters triviales con propiedades enlazables.
Definidores de propiedades vinculables
Para garantizar el correcto funcionamiento del sistema automático de seguimiento de dependencias, todas las rutas de código posibles de un definidor deben escribir en el objeto de propiedad subyacente, incluso si el valor no ha cambiado.
Cualquier otro código en un setter tiene una alta probabilidad de ser incorrecto. Cualquier código que haga actualizaciones basadas en el nuevo valor es muy probablemente un error, ya que este código no se ejecutará cuando la propiedad se cambie a través de un binding.
Por lo tanto, se recomienda utilizar sólo setters triviales con propiedades enlazables.
Escribir en una propiedad vinculable
Las propiedades enlazables informan a sus propiedades dependientes de cada cambio. Esto puede activar gestores de cambios, que a su vez pueden llamar a código arbitrario. Por lo tanto, cada escritura en una propiedad vinculable debe ser inspeccionada cuidadosamente. Pueden producirse los siguientes problemas.
Escritura de valores intermedios en propiedades vinculables
Las propiedades vinculables no deben utilizarse como variables en algoritmos. Cada valor escrito se comunicaría a las propiedades dependientes. Por ejemplo, en el siguiente código, otras propiedades que dependen de myProperty serían informadas primero sobre el cambio a 42, y después sobre el cambio a maxValue.
myProperty = somecomputation(); // returning, say, 42
if (myProperty.value() > maxValue)
myProperty = maxValue;En su lugar, realice el cálculo en una variable independiente. El uso correcto se muestra en el siguiente ejemplo.
int newValue = someComputation(); if (newValue > maxValue) newValue = maxValue; myProperty = newValue; // only write to the property once
Escritura de propiedades enlazables en estados de transición
Cuando una propiedad vinculable es un miembro de una clase, cada escritura en esa propiedad puede exponer el estado actual al exterior. Por tanto, las propiedades vinculables no deben escribirse en estados transitorios, cuando no se cumplen las invariantes de la clase.
Por ejemplo, en una clase que representa un círculo que tiene dos miembros radio y área consistentes, un setter podría tener este aspecto (donde radio es una propiedad bindable):
void setRadius(double newValue)
{
radius = newValue; // this might trigger change handlers
area = M_PI * radius * radius;
emit radiusChanged();
}Aquí, el código disparado en los manejadores de cambio podría utilizar el círculo, mientras que tiene el nuevo radio, pero todavía la antigua área.
Propiedades vinculables con setters y getters virtuales
Los setters y getters de propiedades normalmente deberían ser mínimos y no hacer nada más que establecer la propiedad; por lo tanto, normalmente no es apropiado que tales setters y getters sean virtuales. No tiene sentido que la clase derivada haga nada.
Sin embargo, algunas clases Qt pueden tener propiedades con setters virtuales. Cuando se subclasifica una clase Qt de este tipo, la sobreescritura de dichos setters requiere un cuidado especial.
En cualquier caso, la implementación base debe ser llamada para que el enlace funcione correctamente.
A continuación se ilustra este enfoque.
void DerivedClass::setValue(int val) { // do something BaseClass::setValue(val); // probably do something else }
Todas las reglas y recomendaciones comunes relativas a la escritura de propiedades enlazables también se aplican aquí. En cuanto se llama a la implementación de la clase base, se notifica a todos los observadores el cambio de la propiedad. Esto significa que las invariantes de la clase deben cumplirse antes de llamar a la implementación base.
En los raros casos en los que se necesiten getters o setters virtuales, la clase base debe documentar los requisitos que impone a los overrides.
Formulación de un enlace de propiedad
Cualquier expresión C++ que se evalúe con el tipo correcto puede utilizarse como expresión de vinculación y entregarse al método setBinding(). Sin embargo, para formular un binding correcto, deben seguirse algunas reglas.
El seguimiento de dependencias sólo funciona en propiedades enlazables. Es responsabilidad del desarrollador asegurarse de que todas las propiedades utilizadas en la expresión de vinculación son propiedades vinculables. Cuando se utilizan propiedades no vinculables en una expresión de vinculación, los cambios en esas propiedades no provocan actualizaciones en la propiedad vinculada. No se genera ninguna advertencia o error ni en tiempo de compilación ni en tiempo de ejecución. La propiedad vinculada sólo se actualizará cuando cambien las propiedades vinculables utilizadas en la expresión de vinculación. Las propiedades no vinculables pueden utilizarse en una vinculación si es posible garantizar que se llama a markDirty en la propiedad vinculada en cada cambio de la dependencia no vinculable.
La propiedad vinculada puede evaluar su vinculación varias veces durante su vida. El desarrollador debe asegurarse de que todos los objetos utilizados en la expresión de vinculación vivan más que la vinculación.
El sistema de propiedades vinculables no es seguro para los hilos. Las propiedades utilizadas en la expresión de vinculación en un subproceso no deben ser leídas o modificadas por ningún otro subproceso. Un objeto de una clase derivada de QObject que tenga una propiedad vinculable no debe moverse a otro subproceso. Asimismo, un objeto de una clase derivada de QObject que tenga una propiedad que se utilice en un enlace no debe moverse a otro subproceso. En este contexto, es irrelevante si se utiliza en una vinculación de una propiedad en el mismo objeto o en una vinculación de una propiedad en otro objeto.
La expresión de vinculación no debe leerse de la propiedad para la que es una vinculación. De lo contrario, se creará un bucle de evaluación.
La expresión de vinculación no debe escribir en la propiedad a la que vincula.
Las funciones utilizadas como binding, así como todo el código que se llame dentro de un binding, no deben co_await. Hacerlo puede confundir el seguimiento de dependencias del sistema de propiedades.
Propiedades vinculables y multiproceso
Las propiedades vinculables no son seguras para los hilos, a menos que se indique lo contrario. Una propiedad enlazable no debe ser leída o modificada por ningún subproceso que no sea en el que fue creada.
Seguimiento de propiedades enlazables
En ocasiones, las relaciones entre propiedades no pueden expresarse mediante enlaces. En su lugar, puede que necesite ejecutar código personalizado cada vez que cambie el valor de una propiedad y, en lugar de asignar el valor a otra propiedad, pasarlo a otras partes de su aplicación. Por ejemplo, escribir datos en un socket de red o imprimir la salida de depuración. QProperty proporciona dos mecanismos para el seguimiento.
Puedes registrar una función callback para que sea llamada cada vez que el valor de una propiedad cambie, usando onValueChanged(). Si quieres que la llamada de retorno también sea llamada para el valor actual de la propiedad, registra tu llamada de retorno usando subscribe() en su lugar.
Interacción con Q_PROPERTY
Una Q_PROPERTY que defina BINDABLE puede enlazarse y utilizarse en expresiones de enlace. Puedes implementar tales propiedades usando QProperty, QObjectBindableProperty, o QObjectComputedProperty.
Los Q_PROPERTY sin BINDABLE también se pueden vincular y utilizar en expresiones de vinculación, siempre que definan una señal NOTIFY. Debe envolver la propiedad en un QBindable utilizando el constructor QBindable(QObject* obj, const char* property). A continuación, la propiedad puede vincularse mediante QBindable::setBinding() o utilizarse en una expresión de vinculación mediante QBindable::value(). Debes utilizar QBindable::value() en expresiones de vinculación en lugar de la función normal de propiedad READ (o MEMBER) para habilitar el seguimiento de dependencias si la propiedad no es BINDABLE.
© 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.