Details
-
Bug
-
Resolution: Fixed
-
P1: Critical
-
6.5.0
-
None
-
-
44ca1085d (dev), c83dd11eb (6.5)
Description
If QObject has Q_PROPERTY that's a type of QGADGET, binding in QML don't work as expected. The QML bindings don't update when the value has change or initialize correctly. A work around is to remove BINDABLE from the Q_PROPERTY, but isn't ideal because it looses the benefits of the binding. '
I've attached a minimum example, that demonstrates the bug.
Here's a following example that doesn't work:
import QtQuick import QtQuick.Window import doubleBinding 1.0 Window { width: 640 height: 480 visible: true title: qsTr("Hello World") MouseArea { anchors.fill: parent onClicked: { console.log("clicked") var time = aId.x.timeIndex + 1 aId.x = WeatherModelUrlUtils.url(time); //This assignment works } } A { id: aId x: WeatherModelUrlUtils.url(1); //This binding doesn't work onXChanged: { console.log("x changed:" + aId.x.timeIndex) } } B { id: bId y: aId.x //This binding doesn't work onZChanged: { console.log("z changed:" + bId.z) } } Text { text: "aId.x.timeIndex:" + aId.x.timeIndex + "\nbId.y.timeIndex:" + bId.y.timeIndex + "\nb.z:" + bId.z } Text { anchors.centerIn: parent text: "Click to change the time index" font.pixelSize: 20 } }
The c++ code for A and B:
#ifndef A_H #define A_H #include <QObject> #include <QProperty> #include <QtQml> //Our includes #include "WeatherModelUrl.h" class A : public QObject { Q_OBJECT QML_ELEMENT //This doesn't work. Q_PROPERTY(WeatherModelUrl x READ x WRITE setX NOTIFY xChanged BINDABLE bindableX) //This works // Q_PROPERTY(WeatherModelUrl x READ x WRITE setX NOTIFY xChanged) public: explicit A(QObject *parent = nullptr) : QObject(parent) {} WeatherModelUrl x() const { return m_x.value(); } void setX(const WeatherModelUrl& newX) { m_x.setValue(newX);} QBindable<WeatherModelUrl> bindableX() { return QBindable<WeatherModelUrl>(&m_x); } signals: void xChanged(); private: Q_OBJECT_BINDABLE_PROPERTY(A, WeatherModelUrl, m_x, &A::xChanged) }; class B : public QObject { Q_OBJECT QML_ELEMENT //This doesn't work. Q_PROPERTY(WeatherModelUrl y READ y WRITE setY NOTIFY yChanged BINDABLE bindableY) //This works! // Q_PROPERTY(WeatherModelUrl y READ y WRITE setY NOTIFY yChanged) Q_PROPERTY(int z READ z NOTIFY zChanged) public: explicit B(QObject *parent = nullptr) : QObject(parent) { m_z.setBinding([this]()->int { auto y = m_y.value(); if(y.timeIndex() < 2) { return -1; } return y.timeIndex() * 2; }); } WeatherModelUrl y() const { return m_y.value(); } void setY(const WeatherModelUrl& newY) { m_y.setValue(newY); } QBindable<WeatherModelUrl> bindableY() { return QBindable<WeatherModelUrl>(&m_y); } int z() const { return m_z.value(); } QBindable<int> bindableZ() const { return QBindable<int>(&m_z); } signals: void yChanged(); void zChanged(); private: Q_OBJECT_BINDABLE_PROPERTY(B, WeatherModelUrl, m_y, &B::yChanged) Q_OBJECT_BINDABLE_PROPERTY(B, int, m_z, &B::zChanged) }; #endif // A_H
The WeatherModelUrl Q_GADGET:
#ifndef WEATHERMODELURL_H #define WEATHERMODELURL_H #include <QtQml> #include <QObject> //#include "Result.h" class WeatherModelUrl { Q_GADGET QML_NAMED_ELEMENT(weatherModelUrl) QML_UNCREATABLE("WeatherModelUrl is uncreatable"); Q_PROPERTY(qsizetype timeIndex READ timeIndex CONSTANT) public: WeatherModelUrl() : m_timeIndex(-1) {} WeatherModelUrl(qsizetype timeIdx) : m_timeIndex(timeIdx) {} // Getters qsizetype timeIndex() const { return m_timeIndex; } bool operator ==(const WeatherModelUrl &other) const; bool operator !=(const WeatherModelUrl &other) const; private: qsizetype m_timeIndex; }; QML_DECLARE_TYPE(WeatherModelUrl) QDebug operator<<(QDebug debug, const WeatherModelUrl &url); ////For QML use class WeatherModelUrlUtils : public QObject { Q_OBJECT QML_ELEMENT QML_SINGLETON public: WeatherModelUrlUtils(QObject* parent = nullptr) : QObject(parent) {} Q_INVOKABLE static WeatherModelUrl url(int timeIdx) { return WeatherModelUrl(timeIdx); } }; #endif // WEATHERMODELURL_H