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?
Forum Updated to NodeBB v4.3 + New Features

how to cause view to update based on model change?

Scheduled Pinned Locked Moved Solved QML and Qt Quick
31 Posts 8 Posters 3.7k Views 4 Watching
  • 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.
  • mzimmersM mzimmers

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

    With data not being a QObject, I would add a method to the model that would emit the signal.

    My model is a simple JS array. How does an array emit a signal?

    JoeCFDJ Offline
    JoeCFDJ Offline
    JoeCFD
    wrote on last edited by JoeCFD
    #6
    This post is deleted!
    mzimmersM 1 Reply Last reply
    0
    • JoeCFDJ JoeCFD

      This post is deleted!

      mzimmersM Offline
      mzimmersM Offline
      mzimmers
      wrote on last edited by
      #7

      @JoeCFD that works for a C++ model, but in this instance, my model is just a JS array of strings. I don't have the context from which to make any of those calls.

      JoeCFDJ 1 Reply Last reply
      0
      • mzimmersM mzimmers

        @JoeCFD that works for a C++ model, but in this instance, my model is just a JS array of strings. I don't have the context from which to make any of those calls.

        JoeCFDJ Offline
        JoeCFDJ Offline
        JoeCFD
        wrote on last edited by
        #8

        @mzimmers Sorry my bad.

        1 Reply Last reply
        1
        • mzimmersM mzimmers

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

          With data not being a QObject, I would add a method to the model that would emit the signal.

          My model is a simple JS array. How does an array emit a signal?

          A Offline
          A Offline
          ankou29666
          wrote on last edited by ankou29666
          #9

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

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

          With data not being a QObject, I would add a method to the model that would emit the signal.

          My model is a simple JS array. How does an array emit a signal?

          As I said, you create a method in your model

          Item
          {
              ListModel
              {
                  id: yourListModel
          
                  function emitSignal ()
                  {
                       emit dataChanged ;
                  }
              }
          }
          

          and you ensure that any modification to the data is followed by a call to this method.

          This is basically what I did by the past (but in C++ not in QML).
          but in the future, in order to make it fool proof, I would rather put the data structure as protected or private member of the model, such that any change that has been made outside of the views has to be done through public accessors of the model, that automatically trigger the appropriate signals.

          JonBJ 1 Reply Last reply
          0
          • A ankou29666

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

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

            With data not being a QObject, I would add a method to the model that would emit the signal.

            My model is a simple JS array. How does an array emit a signal?

            As I said, you create a method in your model

            Item
            {
                ListModel
                {
                    id: yourListModel
            
                    function emitSignal ()
                    {
                         emit dataChanged ;
                    }
                }
            }
            

            and you ensure that any modification to the data is followed by a call to this method.

            This is basically what I did by the past (but in C++ not in QML).
            but in the future, in order to make it fool proof, I would rather put the data structure as protected or private member of the model, such that any change that has been made outside of the views has to be done through public accessors of the model, that automatically trigger the appropriate signals.

            JonBJ Offline
            JonBJ Offline
            JonB
            wrote on last edited by JonB
            #10

            @ankou29666 , @mzimmers
            I know nothing about QML! But I don't think emit will work from JavaScript, it's #defined to empty in C++? My guess would be you need to write just dataChanged(...) or maybe yourListModel.dataChanged(...)?

            1 Reply Last reply
            0
            • A Offline
              A Offline
              ankou29666
              wrote on last edited by
              #11

              not impossible, I had made a little pause with Qt and currently rather working on the UI than the code, so it's far from impossible that I make a few bullshits in my code snippets.

              1 Reply Last reply
              0
              • mzimmersM Offline
                mzimmersM Offline
                mzimmers
                wrote on last edited by
                #12

                There's still something missing here -- again, my model isn't a QML list model; it's a simple JS array. According to the docs, this is a valid model. What the docs don't say, though (at least nowhere that I can find), is how such a model can generate a signal to update the view. Maybe this can't be done from an array model, but I imagine that somehow, I can effect the same thing.

                JonBJ 1 Reply Last reply
                0
                • A Offline
                  A Offline
                  ankou29666
                  wrote on last edited by
                  #13

                  I would rather formulate it this way : the doc doesn't say that such JS array data doesn't update the underlying Qt model, if any, and thus, the Qt views.

                  1 Reply Last reply
                  0
                  • mzimmersM mzimmers

                    There's still something missing here -- again, my model isn't a QML list model; it's a simple JS array. According to the docs, this is a valid model. What the docs don't say, though (at least nowhere that I can find), is how such a model can generate a signal to update the view. Maybe this can't be done from an array model, but I imagine that somehow, I can effect the same thing.

                    JonBJ Offline
                    JonBJ Offline
                    JonB
                    wrote on last edited by JonB
                    #14

                    @mzimmers
                    Yes, a JSON array is not going to be able to emit a signal. What is the problem with calling dataChanged() per above whenever you change existing data the underlying JS array? I mean even if we were in C++ and I altered an array or vector or QList or whatever was my model's actual data I would still have to emit dataChanged() explicitly correspondingly.

                    1 Reply Last reply
                    0
                    • mzimmersM mzimmers

                      Hi all -

                      In this code:

                      ListView {
                          model: spaceProxyModel
                          delegate: GridView {
                              required property Space space
                              property var spaceFeatureList: 
                                  sceneModel.featureUuidList(false, space.uuid) // from a C++ function
                              model: spaceFeatureList
                              delegate: Item {
                                  ...
                      

                      The property spaceFeatureList gets updated from another screen, but my view doesn't reflect the change to the model (I've confirmed that spaceFeatureList is indeed being changed). What do I need to do to force the view update?

                      Thanks...

                      GrecKoG Offline
                      GrecKoG Offline
                      GrecKo
                      Qt Champions 2018
                      wrote on last edited by
                      #15

                      Forget the dataChanged() discussion, it is not relevant here as you realized since no QAIM is involved.
                      @mzimmers said in how to cause view to update based on model change?:

                      The property spaceFeatureList gets updated from another screen, but my view doesn't reflect the change to the model (I've confirmed that spaceFeatureList is indeed being changed). What do I need to do to force the view update?

                      Nothing, it is handed automatically if the spaceFeatureList actually changes.
                      Are you sure it does? How did you confirm it?

                      mzimmersM 1 Reply Last reply
                      1
                      • GrecKoG GrecKo

                        Forget the dataChanged() discussion, it is not relevant here as you realized since no QAIM is involved.
                        @mzimmers said in how to cause view to update based on model change?:

                        The property spaceFeatureList gets updated from another screen, but my view doesn't reflect the change to the model (I've confirmed that spaceFeatureList is indeed being changed). What do I need to do to force the view update?

                        Nothing, it is handed automatically if the spaceFeatureList actually changes.
                        Are you sure it does? How did you confirm it?

                        mzimmersM Offline
                        mzimmersM Offline
                        mzimmers
                        wrote on last edited by
                        #16

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

                        How did you confirm it?

                        I have a telltale printout.

                        My main QML file is SceneSetup.qml. On a button press, I push another component (SceneFeatureSelect.qml) onto my stack. I pass the model as a required property. When SceneFeatureSelect changes the list (the model) it uses a callback to notify SceneSetup. My printout in SceneSetup confirms the addition to the model, but my GridView doesn't change.

                        1 Reply Last reply
                        0
                        • GrecKoG Offline
                          GrecKoG Offline
                          GrecKo
                          Qt Champions 2018
                          wrote on last edited by
                          #17

                          @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.

                          mzimmersM 2 Replies Last reply
                          0
                          • GrecKoG GrecKo

                            @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.

                            mzimmersM Offline
                            mzimmersM Offline
                            mzimmers
                            wrote on last edited by
                            #18
                            This post is deleted!
                            1 Reply Last reply
                            0
                            • GrecKoG GrecKo

                              @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.

                              mzimmersM Offline
                              mzimmersM Offline
                              mzimmers
                              wrote on 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.

                              jeremy_kJ 1 Reply Last reply
                              0
                              • mzimmersM Offline
                                mzimmersM Offline
                                mzimmers
                                wrote on last edited by
                                #20

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

                                1 Reply Last reply
                                0
                                • F Offline
                                  F Offline
                                  flaudio
                                  wrote on last edited by flaudio
                                  #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
                                  • mzimmersM Offline
                                    mzimmersM Offline
                                    mzimmers
                                    wrote on 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
                                    • mzimmersM mzimmers

                                      @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.

                                      jeremy_kJ Offline
                                      jeremy_kJ Offline
                                      jeremy_k
                                      wrote on last edited by jeremy_k
                                      #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 mzimmersM 2 Replies Last reply
                                      0
                                      • jeremy_kJ jeremy_k

                                        @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 last edited by ankou29666
                                        #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.

                                        jeremy_kJ 1 Reply Last reply
                                        0
                                        • A ankou29666

                                          @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.

                                          jeremy_kJ Offline
                                          jeremy_kJ Offline
                                          jeremy_k
                                          wrote on last edited by jeremy_k
                                          #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
                                          1

                                          • Login

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