Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. how to cause view to update based on model change?

how to cause view to update based on model change?

Scheduled Pinned Locked Moved Solved QML and Qt Quick
31 Posts 8 Posters 3.2k 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.
  • G GrecKo
    30 Sept 2024, 07:41

    @mzimmers said in how to cause view to update based on model change?:

    My printout in SceneSetup confirms the addition to the model, but my GridView doesn't change.

    To which model?
    That's too abstract for me to make the link with your code.
    Does or does not the spaceFeatureList get reevaluated with a new value?
    You didn't confirm it with onSpaceFeatureListChanged: print("space feature list changed" if I understand correctly.

    In the following code:

    ListView {
        model: spaceProxyModel
        delegate: GridView {
            required property Space space
            property var spaceFeatureList: 
                sceneModel.featureUuidList(false, space.uuid)
    

    The spaceFeatureList will get reevaluated if you change if sceneModel corresponding NOTIFY signal is emitted or if there's a dataChanged signal for the uuid role of the corresponding row in spaceProxyModel (or for a modelResest or layoutChanged).
    It also may work if featureUuidList is a function implemented in JS but I doubt that's the case.

    M Offline
    M Offline
    mzimmers
    wrote on 30 Sept 2024, 17:02 last edited by
    #19

    @GrecKo said in how to cause view to update based on model change?:

    To which model?
    That's too abstract for me to make the link with your code.

    Sorry - this is a fairly complicated bit of stuff. Here's a snippet of the relevant code:

    Pane {
        id: sceneSetupPane
        signal featureListChanged(spaceId: string, activityId: string, status: bool)
        function changeCallback(spaceId, activityId, status)
        {
            featureListChanged(spaceId, activityId, status)
        }
        ListView {
            model: spaceProxyModel
            delegate: GridView {
                required property Space space
                property var spaceFeatureList: sceneModel.featureUuidList(false, space.uuid)
                model: spaceFeatureList
                delegate: Text { text: modelData }
    
                Connections {
                    target: sceneSetupPane
                    function onFeatureListChanged(spaceId, activityId, status) {
                        if (spaceId.localeCompare(space.uuid) === 0) {
                            if (status) {
                                if (spaceFeatureList.indexOf(activityId) === -1) {
                                    var i = spaceFeatureList.push(activityId)
                                }
                            } else {
                                var j = spaceFeatureList.indexOf(activityId)
                                if (j !== -1) {
                                    spaceFeatureList.splice(spaceFeatureList.indexOf(activityId), 1)
                                }
                            }
                            console.log("spaceFeatureList is now", spaceFeatureList)
                        }
                    }
                }
    

    I've verified that the callback is invoked appropriately, all the arguments are good, and the spaceFeatureList is correct after processing. Yet, my view doesn't change.

    J 1 Reply Last reply 3 Oct 2024, 20:56
    0
    • M Offline
      M Offline
      mzimmers
      wrote on 3 Oct 2024, 04:33 last edited by
      #20

      BTT: anyone have an idea on this? Thanks...

      1 Reply Last reply
      0
      • F Offline
        F Offline
        flaudio
        wrote on 3 Oct 2024, 13:53 last edited by flaudio 10 Mar 2024, 14:04
        #21

        Hi @mzimmers,
        have you tried to use only the GridView (comment the ListView, in order to reduce the amount of code to test)? I suggest also to add the snippet of the used model (I'm not sure if it is a JS list or a ListModel - the featureUuidList method). And try to add (as @GrecKo suggested) onSpaceFeatureListChanged to understand if the GridView model is changing.

        GridView {
            required property Space space
            property var spaceFeatureList: sceneModel.featureUuidList(false, space.uuid)
            model: spaceFeatureList
            delegate: Text { text: modelData }
            onSpaceFeatureListChanged: {
                console.log("model changed!")
           }
        }
        
        1 Reply Last reply
        0
        • M Offline
          M Offline
          mzimmers
          wrote on 3 Oct 2024, 17:53 last edited by
          #22

          @flaudio featureUuidList is an invokable C++ function. I only use it to initialize my property/model.

          I did put the telltale in, and it's not getting hit. Tried it with and without the outer ListView. I'm clearly doing something wrong, because other telltales confirm that the array is indeed getting changed.

          1 Reply Last reply
          0
          • M mzimmers
            30 Sept 2024, 17:02

            @GrecKo said in how to cause view to update based on model change?:

            To which model?
            That's too abstract for me to make the link with your code.

            Sorry - this is a fairly complicated bit of stuff. Here's a snippet of the relevant code:

            Pane {
                id: sceneSetupPane
                signal featureListChanged(spaceId: string, activityId: string, status: bool)
                function changeCallback(spaceId, activityId, status)
                {
                    featureListChanged(spaceId, activityId, status)
                }
                ListView {
                    model: spaceProxyModel
                    delegate: GridView {
                        required property Space space
                        property var spaceFeatureList: sceneModel.featureUuidList(false, space.uuid)
                        model: spaceFeatureList
                        delegate: Text { text: modelData }
            
                        Connections {
                            target: sceneSetupPane
                            function onFeatureListChanged(spaceId, activityId, status) {
                                if (spaceId.localeCompare(space.uuid) === 0) {
                                    if (status) {
                                        if (spaceFeatureList.indexOf(activityId) === -1) {
                                            var i = spaceFeatureList.push(activityId)
                                        }
                                    } else {
                                        var j = spaceFeatureList.indexOf(activityId)
                                        if (j !== -1) {
                                            spaceFeatureList.splice(spaceFeatureList.indexOf(activityId), 1)
                                        }
                                    }
                                    console.log("spaceFeatureList is now", spaceFeatureList)
                                }
                            }
                        }
            

            I've verified that the callback is invoked appropriately, all the arguments are good, and the spaceFeatureList is correct after processing. Yet, my view doesn't change.

            J Offline
            J Offline
            jeremy_k
            wrote on 3 Oct 2024, 20:56 last edited by jeremy_k 10 Mar 2024, 20:57
            #23

            @mzimmers said in how to cause view to update based on model change?:

                                            var i = spaceFeatureList.push(activityId)
            

            I've verified that the callback is invoked appropriately, all the arguments are good, and the spaceFeatureList is correct after processing. Yet, my view doesn't change.

            This code is modifying the content of the javascript array, which does not have a change notification signal. If the goal is to stick with a non-QAbstractItemModel model, the property value needs to change. ie

               var newArray = manipulate(spaceFeatureList);
               spaceFeatureList =  newArray;
            

            Doing this will cause the view to reset and create new delegates for all items visible. The documentation apparently isn't sufficiently explicit. Assigning a JS object to a view's model property does not create a QAIM that tracks the object's content.

            Consider this example:

            import QtQuick
            
            Window {
                ListView {
                    id: view
                    anchors.fill: parent
                }
                ListModel {
                    id: listModel
                    ListElement {
                        modelData: 1
                    }
                }
                property var arrayModel: ["1"]
                property int intModel: 1
            
                Component.onCompleted: {
                    var propList = []
                    for (var i in view.model) {
                        propList.push(i);
                    }
                    console.log("view.model properties:")
                    for (i in propList.sort())
                        console.log(propList[i]);
                }
            

            If view.model is bound to listModel, the output is:

            qml: view.model properties:
            [...]
            qml: count
            qml: countChanged
            qml: data
            qml: dataChanged
            [...]
            

            view.model is bound to arrayModel:

            qml: view.model properties:
            qml: 0
            

            to intModel:

            qml: view.model properties:
            

            Asking a question about code? http://eel.is/iso-c++/testcase/

            A M 2 Replies Last reply 3 Oct 2024, 21:14
            0
            • J jeremy_k
              3 Oct 2024, 20:56

              @mzimmers said in how to cause view to update based on model change?:

                                              var i = spaceFeatureList.push(activityId)
              

              I've verified that the callback is invoked appropriately, all the arguments are good, and the spaceFeatureList is correct after processing. Yet, my view doesn't change.

              This code is modifying the content of the javascript array, which does not have a change notification signal. If the goal is to stick with a non-QAbstractItemModel model, the property value needs to change. ie

                 var newArray = manipulate(spaceFeatureList);
                 spaceFeatureList =  newArray;
              

              Doing this will cause the view to reset and create new delegates for all items visible. The documentation apparently isn't sufficiently explicit. Assigning a JS object to a view's model property does not create a QAIM that tracks the object's content.

              Consider this example:

              import QtQuick
              
              Window {
                  ListView {
                      id: view
                      anchors.fill: parent
                  }
                  ListModel {
                      id: listModel
                      ListElement {
                          modelData: 1
                      }
                  }
                  property var arrayModel: ["1"]
                  property int intModel: 1
              
                  Component.onCompleted: {
                      var propList = []
                      for (var i in view.model) {
                          propList.push(i);
                      }
                      console.log("view.model properties:")
                      for (i in propList.sort())
                          console.log(propList[i]);
                  }
              

              If view.model is bound to listModel, the output is:

              qml: view.model properties:
              [...]
              qml: count
              qml: countChanged
              qml: data
              qml: dataChanged
              [...]
              

              view.model is bound to arrayModel:

              qml: view.model properties:
              qml: 0
              

              to intModel:

              qml: view.model properties:
              
              A Offline
              A Offline
              ankou29666
              wrote on 3 Oct 2024, 21:14 last edited by ankou29666 10 Mar 2024, 21:17
              #24

              @jeremy_k said in how to cause view to update based on model change?:

              Doing this will cause the view to reset and create new delegates for all items visible. The documentation apparently isn't sufficiently explicit. Assigning a JS object to a view's model property does not create a QAIM that tracks the object's content.

              from my own experience, QAbstractItemModel doesn't magically track the object content. If you modify the underlying data out of the QAIM or any connected view, the QAIM is unaware of these changes. I mean that if a QAIM encapsulates a QList<WhatSoEver> list, and you modify that list directly, the QAIM won't see the change.

              I'm getting a little bit confused now, but as OP has two views and the problem is, if I understand it right, when one view is modified, the other is not updated.

              What I would make is an underlying ListModel that would encapsulate the JSArray. And bind those two view to that ListModel rather than to the JSArray. Perhaps wouldn't solve everything, a good beginning I think.

              @GrecKo said in how to cause view to update based on model change?:

              Forget the dataChanged() discussion, it is not relevant here as you realized since no QAIM is involved.

              Yep my bad, I really don't understand where I got a dataChanged signal to ListModel, it doesn't have any.

              J 1 Reply Last reply 3 Oct 2024, 21:54
              0
              • A ankou29666
                3 Oct 2024, 21:14

                @jeremy_k said in how to cause view to update based on model change?:

                Doing this will cause the view to reset and create new delegates for all items visible. The documentation apparently isn't sufficiently explicit. Assigning a JS object to a view's model property does not create a QAIM that tracks the object's content.

                from my own experience, QAbstractItemModel doesn't magically track the object content. If you modify the underlying data out of the QAIM or any connected view, the QAIM is unaware of these changes. I mean that if a QAIM encapsulates a QList<WhatSoEver> list, and you modify that list directly, the QAIM won't see the change.

                I'm getting a little bit confused now, but as OP has two views and the problem is, if I understand it right, when one view is modified, the other is not updated.

                What I would make is an underlying ListModel that would encapsulate the JSArray. And bind those two view to that ListModel rather than to the JSArray. Perhaps wouldn't solve everything, a good beginning I think.

                @GrecKo said in how to cause view to update based on model change?:

                Forget the dataChanged() discussion, it is not relevant here as you realized since no QAIM is involved.

                Yep my bad, I really don't understand where I got a dataChanged signal to ListModel, it doesn't have any.

                J Offline
                J Offline
                jeremy_k
                wrote on 3 Oct 2024, 21:54 last edited by jeremy_k 10 Mar 2024, 21:56
                #25

                @ankou29666 said in how to cause view to update based on model change?:

                @jeremy_k said in how to cause view to update based on model change?:

                Doing this will cause the view to reset and create new delegates for all items visible. The documentation apparently isn't sufficiently explicit. Assigning a JS object to a view's model property does not create a QAIM that tracks the object's content.

                from my own experience, QAbstractItemModel doesn't magically track the object content. If you modify the underlying data out of the QAIM or any connected view, the QAIM is unaware of these changes. I mean that if a QAIM encapsulates a QList<WhatSoEver> list, and you modify that list directly, the QAIM won't see the change.

                Put more abstractly, if code avoids the interface of a class, it won't function as an instance of that class. Hopefully that's not a surprise.

                I'm getting a little bit confused now, but as OP has two views and the problem is, if I understand it right, when one view is modified, the other is not updated.

                What I would make is an underlying ListModel that would encapsulate the JSArray. And bind those two view to that ListModel rather than to the JSArray. Perhaps wouldn't solve everything, a good beginning I think.

                At the risk of repeating, a JS Array (or any javascript object) does not have a change notification signal. This means that nothing else is aware that an update is required. Detecting changes must therefore depend on another mechanism. For example:

                • Assign a new object to a QtObject's property when the change is made.
                • Run a Timer and go looking for changes in data, refreshing display Items as appropriate.
                • Limit changes to a dedicated editor "page" that doesn't rely on change signals, and reload all other "pages" when the editor page is destroyed.

                There are probably other options that I haven't considered.

                The TL;DR; version is: Why not use ListModel? It has member functions for inserting, removing, and modifying items in the list that can be called from javascript.

                Asking a question about code? http://eel.is/iso-c++/testcase/

                A 1 Reply Last reply 3 Oct 2024, 22:17
                1
                • J jeremy_k
                  3 Oct 2024, 21:54

                  @ankou29666 said in how to cause view to update based on model change?:

                  @jeremy_k said in how to cause view to update based on model change?:

                  Doing this will cause the view to reset and create new delegates for all items visible. The documentation apparently isn't sufficiently explicit. Assigning a JS object to a view's model property does not create a QAIM that tracks the object's content.

                  from my own experience, QAbstractItemModel doesn't magically track the object content. If you modify the underlying data out of the QAIM or any connected view, the QAIM is unaware of these changes. I mean that if a QAIM encapsulates a QList<WhatSoEver> list, and you modify that list directly, the QAIM won't see the change.

                  Put more abstractly, if code avoids the interface of a class, it won't function as an instance of that class. Hopefully that's not a surprise.

                  I'm getting a little bit confused now, but as OP has two views and the problem is, if I understand it right, when one view is modified, the other is not updated.

                  What I would make is an underlying ListModel that would encapsulate the JSArray. And bind those two view to that ListModel rather than to the JSArray. Perhaps wouldn't solve everything, a good beginning I think.

                  At the risk of repeating, a JS Array (or any javascript object) does not have a change notification signal. This means that nothing else is aware that an update is required. Detecting changes must therefore depend on another mechanism. For example:

                  • Assign a new object to a QtObject's property when the change is made.
                  • Run a Timer and go looking for changes in data, refreshing display Items as appropriate.
                  • Limit changes to a dedicated editor "page" that doesn't rely on change signals, and reload all other "pages" when the editor page is destroyed.

                  There are probably other options that I haven't considered.

                  The TL;DR; version is: Why not use ListModel? It has member functions for inserting, removing, and modifying items in the list that can be called from javascript.

                  A Offline
                  A Offline
                  ankou29666
                  wrote on 3 Oct 2024, 22:17 last edited by
                  #26

                  @jeremy_k said in how to cause view to update based on model change?:

                  At the risk of repeating, a JS Array (or any javascript object) does not have a change notification signal. This means that nothing else is aware that an update is required. Detecting changes must therefore depend on another mechanism. For example:

                  This is exactly what I said in my first replies.

                  Makes me feel like if in C++ you would have two QAIM each referring to the same dataset and changes made by one model wouldn't notify the other one. Excepted that here we have no model at all, it's a little bit as if we had two distinct models referring to the very same dataset (maybe there's two implicit models created by the views ? doesn't really matter).

                  Creating one explicit model that encapsulates the dataset seems to me the most appropriated solution to the problem. Seems to me like the solution that matches best the model/view architecture.

                  J J 2 Replies Last reply 3 Oct 2024, 22:44
                  1
                  • A ankou29666
                    3 Oct 2024, 22:17

                    @jeremy_k said in how to cause view to update based on model change?:

                    At the risk of repeating, a JS Array (or any javascript object) does not have a change notification signal. This means that nothing else is aware that an update is required. Detecting changes must therefore depend on another mechanism. For example:

                    This is exactly what I said in my first replies.

                    Makes me feel like if in C++ you would have two QAIM each referring to the same dataset and changes made by one model wouldn't notify the other one. Excepted that here we have no model at all, it's a little bit as if we had two distinct models referring to the very same dataset (maybe there's two implicit models created by the views ? doesn't really matter).

                    Creating one explicit model that encapsulates the dataset seems to me the most appropriated solution to the problem. Seems to me like the solution that matches best the model/view architecture.

                    J Offline
                    J Offline
                    jeremy_k
                    wrote on 3 Oct 2024, 22:44 last edited by jeremy_k 10 Mar 2024, 22:45
                    #27

                    @ankou29666 said in how to cause view to update based on model change?:

                    @jeremy_k said in how to cause view to update based on model change?:

                    At the risk of repeating, a JS Array (or any javascript object) does not have a change notification signal. This means that nothing else is aware that an update is required. Detecting changes must therefore depend on another mechanism. For example:

                    This is exactly what I said in my first replies.

                    I would guess that this topic is in the top 5 most common on the board. It's all repetition to me. People ask all sorts of variations, such as What if the variable is called y instead of x? The fundamental issues are the same.

                    Makes me feel like if in C++ you would have two QAIM each referring to the same dataset and changes made by one model wouldn't notify the other one. Excepted that here we have no model at all, it's a little bit as if we had two distinct models referring to the very same dataset (maybe there's two implicit models created by the views ? doesn't really matter).

                    Yes!

                    Creating one explicit model that encapsulates the dataset seems to me the most appropriated solution to the problem. Seems to me like the solution that matches best the model/view architecture.

                    Yes!!

                    Asking a question about code? http://eel.is/iso-c++/testcase/

                    1 Reply Last reply
                    0
                    • A ankou29666
                      3 Oct 2024, 22:17

                      @jeremy_k said in how to cause view to update based on model change?:

                      At the risk of repeating, a JS Array (or any javascript object) does not have a change notification signal. This means that nothing else is aware that an update is required. Detecting changes must therefore depend on another mechanism. For example:

                      This is exactly what I said in my first replies.

                      Makes me feel like if in C++ you would have two QAIM each referring to the same dataset and changes made by one model wouldn't notify the other one. Excepted that here we have no model at all, it's a little bit as if we had two distinct models referring to the very same dataset (maybe there's two implicit models created by the views ? doesn't really matter).

                      Creating one explicit model that encapsulates the dataset seems to me the most appropriated solution to the problem. Seems to me like the solution that matches best the model/view architecture.

                      J Online
                      J Online
                      JoeCFD
                      wrote on 3 Oct 2024, 23:06 last edited by
                      #28

                      @ankou29666 I agree with Ankou. It is simple and easier to create a C++ model.

                      1 Reply Last reply
                      0
                      • A Offline
                        A Offline
                        ankou29666
                        wrote on 3 Oct 2024, 23:11 last edited by
                        #29

                        C++ why not, but if OP's data source is a JS array, I would personally first try something around QML's ListModel before switching to C++

                        1 Reply Last reply
                        0
                        • J jeremy_k
                          3 Oct 2024, 20:56

                          @mzimmers said in how to cause view to update based on model change?:

                                                          var i = spaceFeatureList.push(activityId)
                          

                          I've verified that the callback is invoked appropriately, all the arguments are good, and the spaceFeatureList is correct after processing. Yet, my view doesn't change.

                          This code is modifying the content of the javascript array, which does not have a change notification signal. If the goal is to stick with a non-QAbstractItemModel model, the property value needs to change. ie

                             var newArray = manipulate(spaceFeatureList);
                             spaceFeatureList =  newArray;
                          

                          Doing this will cause the view to reset and create new delegates for all items visible. The documentation apparently isn't sufficiently explicit. Assigning a JS object to a view's model property does not create a QAIM that tracks the object's content.

                          Consider this example:

                          import QtQuick
                          
                          Window {
                              ListView {
                                  id: view
                                  anchors.fill: parent
                              }
                              ListModel {
                                  id: listModel
                                  ListElement {
                                      modelData: 1
                                  }
                              }
                              property var arrayModel: ["1"]
                              property int intModel: 1
                          
                              Component.onCompleted: {
                                  var propList = []
                                  for (var i in view.model) {
                                      propList.push(i);
                                  }
                                  console.log("view.model properties:")
                                  for (i in propList.sort())
                                      console.log(propList[i]);
                              }
                          

                          If view.model is bound to listModel, the output is:

                          qml: view.model properties:
                          [...]
                          qml: count
                          qml: countChanged
                          qml: data
                          qml: dataChanged
                          [...]
                          

                          view.model is bound to arrayModel:

                          qml: view.model properties:
                          qml: 0
                          

                          to intModel:

                          qml: view.model properties:
                          
                          M Offline
                          M Offline
                          mzimmers
                          wrote on 3 Oct 2024, 23:37 last edited by
                          #30
                          This post is deleted!
                          1 Reply Last reply
                          0
                          • M Offline
                            M Offline
                            mzimmers
                            wrote on 4 Oct 2024, 01:36 last edited by
                            #31

                            Thanks for all the help, guys. I was able to get this working like so:

                            GridView {
                                required property Space space
                                property var spaceFeatureList: sceneModel.featureUuidList(false, space.uuid)
                                ListModel {
                                    id: spaceFeatureListModel
                                }
                                Component.onCompleted: {
                                    spaceFeatureListModel.clear()
                                    for (var i = 0; i < spaceFeatureList.length; i++) {
                                        spaceFeatureListModel.append({ "uuid": spaceFeatureList[i] })
                                    }
                                }
                                model: spaceFeatureListModel
                                
                                Connections {
                                    target: sceneSetupPane
                                    function onFeatureListChanged(spaceId, activityId, status) {
                                        if (spaceId.localeCompare(space.uuid) === 0) {
                                            if (status) {
                                                var i
                                                for (i = 0; i < spaceFeatureListModel.count; i++) {
                                                    if (spaceFeatureListModel.get(i).uuid === activityId) {
                                                        break
                                                    }
                                                }
                                                if (i >= spaceFeatureListModel.count) {
                                                    spaceFeatureListModel.append({ "uuid": activityId })
                                                }
                                            } else {
                                                for (i = 0; i < spaceFeatureListModel.count; i++) {
                                                    if (spaceFeatureListModel.get(i).uuid === activityId) {
                                                        spaceFeatureListModel.remove(i)
                                                        break
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            

                            The function could probably use some optimizing, but it works. Thanks again.

                            1 Reply Last reply
                            1
                            • M mzimmers has marked this topic as solved on 4 Oct 2024, 01:36

                            28/31

                            3 Oct 2024, 23:06

                            • Login

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