Auto re-run property bound code in C++ outside of a class?
-
How do we re-run a piece of code -- an expression reading bindable properties -- outside of a class?
I saw the Bindable Properties Example, but it only shows how to subscribe to individual bindable properties outside of class code.
These days, the JavaScript world makes this really easy. For example, with Solid.js "signals" (not to be confused with Qt signals, "signals" in the JS world are a new term that essentially are what Qt bindable properties are, but not exclusive to classes, more generically usable outside of any class), we can make reactivity like this:
// Make a "bindable property" (for sake of describing this in Qt terminology): const [value, setValue] = createSignal(0) // Every second, increment the value: setInterval(() => { setValue(value() + 1) }, 1000) // "Subscribe" to the "bindable property" (Qt terms): createEffect(() => { console.log('value: ', value()) })
Now, any time the
value
"signal" (using Solid.js terms) is updated once per second, the "effect" (Solid.js terms) automatically re-runs because it tracked that thevalue
"signal" was a dependency of the effect.To "subscribe" (in Qt terms) to more than one "signal" (in Solid.js terms), it is really easy:
// "Subscribe" to two "bindable properties" (in Qt terms): // Create an "effect" that re-run based on two "signals" (Solid.js terms): createEffect(() => { console.log('value: ', someValue(), otherValue()) })
Here the effect (Solid.js terms) re-runs any time that
someValue
orotherValue
change.In the Bindable Properties Example, there was not an example of how to achieve the same as Solid.js "effects" outside of a class. This was the only subscription example:
auto priceChangeHandler = subscription.bindablePrice().subscribe([&] { priceDisplay->setText(QString::number(subscription.price())); }); auto priceValidHandler = subscription.bindableIsValid().subscribe([&] { priceDisplay->setEnabled(subscription.isValid()); });
But this is not as ergonomic as computed members in a Qt class (just use any bindable properties inside the expression), or as ergonomic as Solid.js effects (just use any signal inside an effect).
Is there an equivalent Qt API for making a piece of code that re-runs based on its dependencies outside of a class?
In JavaScript we have decorators. We can also take primitives like Solid.js signals and using classy-solid apply these features to class properties. For example:
@reactive class Thing { @signal value = 0 } const t = new Thing() // increment the "signal property" (Solidjs-based terms) every second setInterval(() => t.value++, 1000) // Log any time it changes (still decoupled from the concept of a class) createEffect(() => console.log(t.value))
I'm curious to see if there's a Qt C++ equivalent of this JavaScript example.
On a related note, is there a way to make an "effect" in Qt JavaScript, outside of QML expressions (similar to Solid.js)?
EDIT: Ah, looks like
Qt.binding
is similar, but looks like it needs to be assigned onto a QML property:qmlProp = Qt.binding(function() { // re-runs any time x or width change return x + width })
Is there something standalone that does not need to be a binding for a QML property? This pattern is useful for data manipulation and reactivity; it would be useful purely on the JS side when even no QML is involved.
-
In Qt, property bindings require properties (Q_PROPERTY) and that is only available in subclasses of QObject. So, there is no way to make this work outside of a class.
Unless you mean that a binding should react to changes in another subclass of QObject, then it should be possible.
-
@sierdzio Have a look at Solid.js. People are loving the dependency-tracking reactive patterns. Making the pattern be standalone would be really powerful: it would allow people to use the same pattern with plain data manipulation as they already do inside QML components; really useful for reactive state that lives outside the templates/components.
The current QML equivalent but written in Solid.js with HTML instead of QML is this:
function Component() { const [count, setCount] = createSignal(0) setInterval(() => setCount(count() + 1), 1000) return <div>The count is: {count()}</div> }
In Solid.js, you can easily and simply hoist your reactive state outside a component and share it with multiple components:
const [count, setCount] = createSignal(0) setInterval(() => setCount(count() + 1), 1000) // logs count every second: createEffect(() => console.log(count())) function Component1() { return <div>The count is: {count()}</div> } function Component2() { return <div>The count is: {count()}</div> }
What is the Qt equivalent of moving state outside of a QML component and both:
- logging it when it changes
- rendering it inside two different compoments
?
-
@trusktr said in Auto re-run property bound code in C++ outside of a class?:
@sierdzio Have a look at Solid.js. People are loving the dependency-tracking reactive patterns.
What does that have to do with me? ;-)
What is the Qt equivalent of moving state outside of a QML component and both:
- logging it when it changes
- rendering it inside two different compoments
?
Typically this involves creating a C++ object which holds the state, has properties and signals etc. This solves both of your points nicely and keeps UI and logic nicely separated.