Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. QML model updating without binding loops / correct behavior
Qt 6.11 is out! See what's new in the release blog

QML model updating without binding loops / correct behavior

Scheduled Pinned Locked Moved Unsolved General and Desktop
1 Posts 1 Posters 456 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • C Offline
    C Offline
    centz
    wrote on last edited by centz
    #1

    Hey,

    Suppose I I have the following model:

    ListModel {
        id: actionModel
    
        ListElement {
            name: "Check 1"
            text: "Performing Check 1..."
            check: function () {console.log("check1"); return true}
            status: "waiting"
        }
        ListElement {
            name: "Check 2"
            text: "Performing Check 2... "
            check: function () {console.log("check2"); return false}
            status: "waiting"
        }
        ListElement {
            name: "Check 2"
            text: "Performing Check 3..."
            check: function () {console.log("check3"); return false}
            status: "waiting"
        }
    }
    

    to be displayed by a ListView and the following delegate

    Component {
        id: actionDelegate
        Item {
            width: parent.width
            height: 40
            anchors.horizontalCenter: parent.horizontalCenter
    
            property string myStatus: model.status
            property int myIndex: model.index
            property string myText: model.text
    
            function updateStatus(index, newStatus) {
                modelsContainer.actionModel.setProperty(index, "status", newStatus);
            }
    
            onMyStatusChanged: {
                if (myStatus === "waiting") {
                    state = "waiting";
                } else if (myStatus === "running") {
                    state = "running";
                    var result = model.check();
    
                    if (result) {
                        state = "passed"
                        Qt.callLater(updateStatus, myIndex, "passed")
    

    // updateStatus(updateStatus, myIndex, "passed")
    }
    else {
    state = "failed"
    Qt.callLater(updateStatus, myIndex, "failed")
    }

                    if (result && myIndex < (modelsContainer.actionModel.count - 1)) {
                        Qt.callLater(updateStatus, myIndex + 1, "running");
                    }
    
                } else if (myStatus === "passed") {
                    state = "passed";
                } else if (myStatus === "failed") {
                    state = "failed";
                }
            }
    
            states: [
                State {
                    name: "waiting"
                    PropertyChanges {
                        target: myTextField
                        color: "grey"
                        text: "waiting"
                    }
                    // Define properties and behaviors for 'waiting' state
                },
                State {
                    name: "running"
                    PropertyChanges {
                        target: myTextField
                        color: "blue"
                        text: "running"
                    }
                    // Define properties and behaviors for 'running' state
                },
                State {
                    name: "passed"
                    PropertyChanges {
                        target: myTextField
                        color: "green"
                        text: "passed"
                    }
                    // Define properties and behaviors for 'passed' state
                },
                State {
                    name: "failed"
                    PropertyChanges {
                        target: myTextField
                        color: "red"
                        text: "failed"
                    }
                    // Define properties and behaviors for 'failed' state
                }
            ]
    
            MyTextField {
                id: myTextField
                text: myText
                color: "grey"
            }
        }
    }.
    

    The status field/role/whatever of the ListElement is bound to a local property called myStatus through:

    property string myStatus: model.status.

    The intended semantics are: display the list in a waiting status. Trigger a series of checks depending on whether the previous check has completed. On pass check trigger the next. On fail stop everything.

    Now an issue appears:
    Writing an onMyStatusHandler results in a binding loop, if I try to to update the model synchronously when some change happens because the handler itself is also modifying the model.

    I guess this is a result of trying to place the "on initial trigger logic, do the test and if that passes move to the next" inside some handler. Although offloading this to a separate function will likely also trigger a binding loop...

    The intended unidirectional semantics would be:
    1)capture the change
    2) while handling the change break the binding(?)
    3) calculate the new state <- this step must not trigger a new Change handler
    4) store the new state and restore the binding.

    A temporary hack is to try to (1) modify the local state on model change (2) use Qt.callLater to update the model after the handler without triggering a loop. But the solution feels hacky.

    Is there a recommended API and/or solution for this problem?

    1 Reply Last reply
    0

    • Login

    • Login or register to search.
    • First post
      Last post
    0
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    • Get Qt Extensions
    • Unsolved