Dan M.
Dan M.

Reputation: 4052

Why Qt QML doesn't properly bind properties?

It's very weird because I use almost the same pattern in another QML file and it works correctly! I'm sure I'm not reassigning value anywhere (the only thing I know that can make property static)! I have something like this (it's a simplified example and probably works as expected):

Item {
    property bool isExpanded: false
    MouseArea {
        anchors.fill: parent
        onClicked: {
            isExpanded = !isExpanded
            console.log(isExpanded)
            console.log(myId.visible)
        }
    }
    MyCustomItem {
        id: myId
        visible: isExpanded
        // other stuff
    }
}

IsExpanded changes after I click but Item visibility always stays the same! And I have many other properties for my item (for example height: isExpanded ? someval : 0) which doesn't change too! It sort of works if I always change everything manually but whats the point then? And in another QML I use a similar pattern and there it works!

BUT! If I put, for example onDoubleClicked/another button press: myId.visible = Qt.binding(function() {return isExpanded}) it works as it should! So, for some unknown reason it doesn't 'bind' them when it should in regular property declaration (visible: isExpanded).

So the question is, do I really need to explicitly tell Qt to bind property to make it work?

Edit: To make clear: I'm certain I don't reassign visible property anywhere. I doble checked it. And though qml size is only about 100 of lines I used ctrl+f to find any mention of visible and found none except already mentioned. If there is more reliable way to tell what's from/ check for possible reassignments somewhere or something, please tell.

Upvotes: 1

Views: 3946

Answers (2)

Dan M.
Dan M.

Reputation: 4052

I found the issue! Very unobvious (haven't found documented anywhere) and it fails silently. The problem is that MyCustomItem contained a property with the same name of outer booleal property isExpanded so in this code:

MyCustomItem {
    id: myId
   visible: isExpanded
 // other stuff
}

isExpanded is from MyCustomItem. But without any indication of that. Well, now I know that this could happen and would probably spot it if it happens again but this is really counter-intuitive. What if it wasn't my item but someone else's and I didn't know that it had such property? Qt creator could've at least warned about possible ambiguities.

Upvotes: 1

cmannett85
cmannett85

Reputation: 22346

So the question is, do I really need to explicitly tell Qt to bind property to make it work?

Yes.

When declaring a property, you can use the binding syntax e.g. visible: isExpanded. But setting a property in imperative code using normal JavaScript syntax (i.e. the assignment operator) will break any existing binding and overwrite the property's value. If you want to explicitly set a property binding in imperative code, use the Qt.binding() method (docs).

Although your actual problem is due to falling victim to QML's scoping rules. Your MyCustomItem type has an isExpanded property, so when you declare:

MyCustomItem {
    id: myId
    visible: isExpanded
    // other stuff
}

You're actually binding visible to MyCustomItem::isExpanded. So to fix just be explicit as to which isExpanded you are referring to:

Item {
    id: base
    property bool isExpanded: false
    MouseArea {
        anchors.fill: parent
        onClicked: {
            isExpanded = !isExpanded
            console.log(isExpanded)
            console.log(myId.visible)
        }
    }
    MyCustomItem {
        id: myId
        visible: base.isExpanded
        // other stuff
    }
}

Upvotes: 4

Related Questions