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. ListView goes up when model updated
Forum Updated to NodeBB v4.3 + New Features

ListView goes up when model updated

Scheduled Pinned Locked Moved Solved QML and Qt Quick
13 Posts 5 Posters 4.9k Views 1 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.
  • dheerendraD Offline
    dheerendraD Offline
    dheerendra
    Qt Champions 2022
    wrote on last edited by
    #2

    Looks like you are updating the currentIndex of the view as well. Because of this it may be moving. Please check. Other wise give sample to check this.

    Dheerendra
    @Community Service
    Certified Qt Specialist
    http://www.pthinks.com

    1 Reply Last reply
    1
    • D DavidM29

      Hello,

      The ListView is going all the way to the top when the model is updated. Is there anyway to avoid this behavior ?

      J.HilkJ Offline
      J.HilkJ Offline
      J.Hilk
      Moderators
      wrote on last edited by
      #3

      @DavidM29 that's normal.

      updating the whole model, instead of the individual items will trigger a complete recreation of the ListView Content -> currentIndex is reset to 0

      You can store the currentIndex in a property and set it on model changed

      ListView {
          property int savedIndex = 0;
          onCurrentIndexChanged: savedIndex = currentIndex //eventually check against != 0 first
         onModelChanged: currentIndex = savedIndex
      }
      

      Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


      Q: What's that?
      A: It's blue light.
      Q: What does it do?
      A: It turns blue.

      JonBJ 1 Reply Last reply
      4
      • J.HilkJ J.Hilk

        @DavidM29 that's normal.

        updating the whole model, instead of the individual items will trigger a complete recreation of the ListView Content -> currentIndex is reset to 0

        You can store the currentIndex in a property and set it on model changed

        ListView {
            property int savedIndex = 0;
            onCurrentIndexChanged: savedIndex = currentIndex //eventually check against != 0 first
           onModelChanged: currentIndex = savedIndex
        }
        
        JonBJ Offline
        JonBJ Offline
        JonB
        wrote on last edited by JonB
        #4

        @J.Hilk
        After a model change the previous savedIndex might be higher than the new number of rows, I don't know whether you have to check for that to avoid an error? Whether it's always sensible to restore the current index if the model change is completely different rows is another question? A different possibility is to save the current item's value/text and then search to restore to that only if present.

        J.HilkJ 1 Reply Last reply
        0
        • JonBJ JonB

          @J.Hilk
          After a model change the previous savedIndex might be higher than the new number of rows, I don't know whether you have to check for that to avoid an error? Whether it's always sensible to restore the current index if the model change is completely different rows is another question? A different possibility is to save the current item's value/text and then search to restore to that only if present.

          J.HilkJ Offline
          J.HilkJ Offline
          J.Hilk
          Moderators
          wrote on last edited by J.Hilk
          #5

          @JonB
          QML is not c++ ;-)
          If you set a current index that is outside the range of the list, it defaults to 0

          The only thing you have to consider/check, if the currentIndex changed signal is emitted before the modelChanged signal. Because you may end up overwriting savedIndex with 0


          Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


          Q: What's that?
          A: It's blue light.
          Q: What does it do?
          A: It turns blue.

          D 1 Reply Last reply
          2
          • J.HilkJ J.Hilk

            @JonB
            QML is not c++ ;-)
            If you set a current index that is outside the range of the list, it defaults to 0

            The only thing you have to consider/check, if the currentIndex changed signal is emitted before the modelChanged signal. Because you may end up overwriting savedIndex with 0

            D Offline
            D Offline
            DavidM29
            wrote on last edited by
            #6

            @J.Hilk
            Thank you for your suggestion I'm going to try that. It seems to be a good solution.

            1 Reply Last reply
            0
            • D Offline
              D Offline
              DavidM29
              wrote on last edited by DavidM29
              #7

              @J-Hilk @JonB @dheerendra
              It does not work on my case obviously. I'll have to find another solution.
              I use a ScrollView and inside this scrollview I do have my ListView. And I believe that when the ListView model is update it also refresh the Scrollview. So whatever index I do have in my ListView it goes back to the top.

              Edit :

              Here is some sample code of the part I'm working with you should be able to run it :

              main.qml :

              import QtQuick 2.0
              import QtQuick.Window 2.2
              import QtQuick.Controls 1.3
              import QtQuick.Controls.Styles 1.3
              
              Window {
                  id: window
                  visible: true
                  width: 640
                  height: 480
                  title: qsTr("Hello World")
              
              
                  ScrollView{
                      id :view
                      anchors.fill: parent
                      flickableItem.flickableDirection: Flickable.VerticalFlick
                      flickableItem.boundsBehavior: Flickable.StopAtBounds
                      flickableItem.interactive: true
              
              
                      style: ScrollViewStyle {
                          handle: Rectangle {
                              implicitWidth: 10
                              implicitHeight: 30
                              radius: 5
                              color: "grey"
                          }
                          scrollBarBackground: Rectangle {
                              implicitWidth: 15
                              color: "transparent"
                          }
                          decrementControl: Rectangle {
                              implicitWidth: 0
                              implicitHeight: 0
              
                          }
                          incrementControl: Rectangle {
                              implicitWidth: 0
                              implicitHeight: 0
                          }
                      }
              
                      ListView {
                          id: dataView
                          
                          property int savedIndex : 0
                          
                          clip: true
                          spacing : 2
                          model: window.model
                          delegate: MyDelegate{}
                          interactive: false
                          
                          onCurrentIndexChanged:{
                              console.log("Current index changed")
                              if(currentIndex !==0){
                                  savedIndex = currentIndex
                              }
                          }
                          onModelChanged: currentIndex = savedIndex
                      }
                  }
              
                  property var model:
                      [
                      {"type": "switch", "optionName": qsTr("Option 1"), "value" : true },
                      {"type": "switch", "optionName": qsTr("Option 2"), "value" : true},
                      {"type": "switch", "optionName": qsTr("Option 3"), "value" : true},
                      {"type": "switch", "optionName": qsTr("Option 4"), "value" : false},
                      {"type": "switch", "optionName": qsTr("Option 5"), "value" : false},
                      {"type": "switch", "optionName": qsTr("Option 6"), "value" : true},
                      {"type": "switch", "optionName": qsTr("Option 7"), "value" : true},
                      {"type": "switch", "optionName": qsTr("Option 8"), "value" :false },
                      {"type": "switch", "optionName": qsTr("Option 9"), "value" : false},
                      {"type": "switch", "optionName": qsTr("Option 10"), "value" : true},
                      {"type": "switch", "optionName": qsTr("Option 11"), "value" : false},
                      {"type": "switch", "optionName": qsTr("Option 12"), "value" : false},
                      {"type": "switch", "optionName": qsTr("Option 13"), "value" : true},
                      {"type": "switch", "optionName": qsTr("Option 14"), "value" : true},
                      {"type": "switch", "optionName": qsTr("Option 15"), "value" : true},
                      {"type": "switch", "optionName": qsTr("Option 16"), "value" : true},
                      {"type": "switch", "optionName": qsTr("Option 17"), "value" : false},
                      {"type": "switch", "optionName": qsTr("Option 18"), "value" : false},
                      {"type": "switch", "optionName": qsTr("Option 19"), "value" : false},
                      {"type": "switch", "optionName": qsTr("Option 20"), "value" : true},
                  ]
              
                  function updateModel(){
                      dataView.model = window.model
                  }
              }
              
              

              MyDelegate.qml

              import QtQuick 2.0
              
              Item {
                  id: multiDelegate
                  height: multiDelegate.ListView.view.height/8
                  width: multiDelegate.ListView.view.width
              
                  property var myItem: model.modelData ? model.modelData : model
                  property int selectLanguage: 1
              
                  function bestDelegate(t) {
                      switch(t){
                      case "switch":
                          return switchDelegate;
                      case "input" :
                          return inputDelegate;
                      default :
                          return console.log("Type not found : " + t)
              
                      }
                  }
              
              
                  Component {
                      id: switchDelegate
                      SwitchOption {
                          id: night1
                          height: parent.height
                          width: parent.width
                          nomOption: myItem.optionName
                          bouton.checked: myItem.value
                          bouton.onCheckedChanged: {
                              window.model[index].value = bouton.checked
                              updateModel()
                          }
                      }
                  }
              
                  Component {
                      id : inputDelegate
                      TextInputOption{
                          id: reglageUsine
                          height: parent.height / 8
                          width: parent.width
                          nomOption: qsTrId(myItem.optionName)
                          unite: myItem.unite
                          text: myItem.value
                          onTextChanged: {
                              window.model[index].value = text
                          }
              
                      }
                  }
              
                  Loader {
                      id: itemDisplay
                      anchors.fill: parent;
                      anchors.topMargin: 2
                      anchors.bottomMargin: 2
                      sourceComponent: bestDelegate(myItem.type)
                  }
              
              }
              
              

              SwitchOption.qml :

              import QtQuick 2.4
              import QtQuick.Controls 1.3
              import QtQuick.Layouts 1.1
              
              
              Item {
              
                  property string nomOption : "Option"
                  property alias bouton : bouton
              
                  RowLayout{
                      width: parent.width
                      height: parent.height
                      spacing: 0
                      Rectangle{
                          Layout.preferredWidth: parent.width*0.54
                          Layout.preferredHeight: parent.height
                          anchors.right: value.left
                          color: "transparent"
                          Rectangle{
                              anchors.fill: parent
                              anchors.leftMargin: parent.width/10
                              color: "transparent"
                              Text {
                                  id: textOption
                                  text: nomOption
                                  font.pixelSize: 15
                                  color: "black"
                                  anchors.verticalCenter: parent.verticalCenter
                              }
                          }
                      }
              
                      Rectangle{
                          id:value
                          Layout.preferredWidth: parent.width*0.45
                          Layout.preferredHeight: parent.height
                          Layout.alignment: Qt.AlignRight
                          color: "transparent"
              
              
                              CheckBox {
                                  id: bouton
                                  height: parent.height*4/7
                                  width: height*2
                                  anchors.horizontalCenter: parent.horizontalCenter
                                  checked:false
                                  anchors.verticalCenter: parent.verticalCenter
                              }
              
                      }
                  }
              }
              
              
              

              TextInputOption.qml :

              import QtQuick 2.4
              import QtQuick.Controls 1.3
              import QtQuick.Controls.Styles 1.3
              import QtQuick.Layouts 1.1
              
              Item {
                  //Le libellé de l'option
                  property string nomOption : "Option"
                  property string unite: ""
                  property string text: ""
              
                  RowLayout{
                      width: parent.width
                      height: parent.height
                      spacing: 0
                      Rectangle{
                          Layout.preferredWidth: parent.width*0.54
                          Layout.preferredHeight: parent.height
                          anchors.right: value.left
                          color: "transparent"
                          Rectangle{
                              anchors.fill: parent
                              anchors.leftMargin: parent.width/10
                              color: "transparent"
                              Text {
                                  id: textOption
                                  text: nomOption
                                  color: "black"
                                  font.pixelSize: 15
                                  anchors.verticalCenter: parent.verticalCenter
                              }
                          }
                      }
              
                      Rectangle{
                          id: value
                          Layout.preferredWidth: parent.width*0.45
                          Layout.preferredHeight: parent.height
                          Layout.alignment: Qt.AlignRight
                          color: "transparent"
              
                          CustomTextField{
                              id:valeur
                              isNumeric: true
                              width: parent.width*0.55
                              height: parent.height*0.8
                              anchors.centerIn: parent
                              input.text: text
                              input.onTextChanged: {
                                  text = input.text
                              }
                          }
              
                          Text{
                              anchors.left : valeur.right
                              anchors.leftMargin: 5
                              anchors.verticalCenter: valeur.verticalCenter
                              font.family: localFont.name
                              color: "black"
                              font.pixelSize: 15
                              text : unite
                          }
                      }
                  }
              }
              
              

              CustomTextField.qml :

              import QtQuick 2.0
              
              
              Item {
              
                  width: 150
                  height: 35
              
                  property bool isNumeric: false
                  property string regex: ""
                  property bool isMDP: false
                  property alias input:textFieldIn
              
                  Rectangle{
                      anchors.fill: parent
                      border.width: 1
                      border.color:textFieldIn.focus ? "red" : "grey"
                      radius: height/2
                      color: "darkgrey"
              
                      TextInput{
                          id: textFieldIn
                          anchors.fill: parent
                          layer.enabled: true
                          font.pixelSize: 15
                          verticalAlignment: TextInput.AlignVCenter
                          horizontalAlignment: TextInput.AlignHCenter
                          cursorVisible: false
                          echoMode: isMDP ? TextInput.Password : TextInput.Normal
                          onFocusChanged: {
                              if (focus) keyboard.setTarget(this, isNumeric, regex, isMDP)
                          }
                      }
              
                  }
              }
              
              

              When I do run this code my ScrollView goes all the way up each time I change a value on my list.
              It is because I update the model. But if I don't update the model I loose the change when the value is out of the view (is not rendered anymore and once it comes back it reload the initial model which is not the right value anymore)

              1 Reply Last reply
              0
              • fcarneyF Offline
                fcarneyF Offline
                fcarney
                wrote on last edited by
                #8

                Why not?
                https://stackoverflow.com/questions/30359262/how-to-scroll-qml-scrollview-to-center

                C++ is a perfectly valid school of magic.

                D 1 Reply Last reply
                0
                • fcarneyF fcarney

                  Why not?
                  https://stackoverflow.com/questions/30359262/how-to-scroll-qml-scrollview-to-center

                  D Offline
                  D Offline
                  DavidM29
                  wrote on last edited by
                  #9

                  @fcarney
                  I don't need to center my view onComplete. I want to keep the position of my ScrollView when updating the model on the ListView.
                  But if you do have any other thought do not hesitate.

                  J.HilkJ 1 Reply Last reply
                  0
                  • D DavidM29

                    @fcarney
                    I don't need to center my view onComplete. I want to keep the position of my ScrollView when updating the model on the ListView.
                    But if you do have any other thought do not hesitate.

                    J.HilkJ Offline
                    J.HilkJ Offline
                    J.Hilk
                    Moderators
                    wrote on last edited by
                    #10

                    @DavidM29
                    I was a tiny bit of, you have to use contentY and not currentIndex. Simply scrolling through a list view apparently does not change the currentIndex at all....

                    this works:

                    import QtQuick 2.12
                    import QtQuick.Controls 2.5
                    
                    ApplicationWindow {
                        id:root
                        visible:true
                        width:500; height:500
                    
                        property bool switchState: false
                        property bool justChanged: false
                    
                        Timer{
                            running:  true
                            interval: 2000
                            repeat: true
                            onTriggered:{
                                justChanged = true;
                                switchState = !switchState
                            }
                        }
                    
                        ListView{
                            id:lView
                            anchors.fill: parent
                    
                            model: root.switchState ? 20 : 22
                    
                            property int savedContentY: 0
                            Component.onCompleted: savedContentY = contentY
                    
                            onModelChanged:{
                                contentY = savedContentY
                            }
                            onContentYChanged:{
                                if(!justChanged) savedContentY = contentY;
                                justChanged = false;
                            }
                    
                            delegate: Text {
                                text: modelData
                    
                                height: 50
                    
                                color: index % 2 ? "red" : "blue"
                            }
                        }
                    }
                    
                    

                    Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


                    Q: What's that?
                    A: It's blue light.
                    Q: What does it do?
                    A: It turns blue.

                    D 1 Reply Last reply
                    1
                    • J.HilkJ J.Hilk

                      @DavidM29
                      I was a tiny bit of, you have to use contentY and not currentIndex. Simply scrolling through a list view apparently does not change the currentIndex at all....

                      this works:

                      import QtQuick 2.12
                      import QtQuick.Controls 2.5
                      
                      ApplicationWindow {
                          id:root
                          visible:true
                          width:500; height:500
                      
                          property bool switchState: false
                          property bool justChanged: false
                      
                          Timer{
                              running:  true
                              interval: 2000
                              repeat: true
                              onTriggered:{
                                  justChanged = true;
                                  switchState = !switchState
                              }
                          }
                      
                          ListView{
                              id:lView
                              anchors.fill: parent
                      
                              model: root.switchState ? 20 : 22
                      
                              property int savedContentY: 0
                              Component.onCompleted: savedContentY = contentY
                      
                              onModelChanged:{
                                  contentY = savedContentY
                              }
                              onContentYChanged:{
                                  if(!justChanged) savedContentY = contentY;
                                  justChanged = false;
                              }
                      
                              delegate: Text {
                                  text: modelData
                      
                                  height: 50
                      
                                  color: index % 2 ? "red" : "blue"
                              }
                          }
                      }
                      
                      
                      D Offline
                      D Offline
                      DavidM29
                      wrote on last edited by
                      #11

                      @J.Hilk
                      I tried to adapt your solution to my problem. But it does not work.
                      I'm not sure that the use of the Scrollview is compatible with your solution.

                      I don't know what cause the Scrollview to go all the way up when my listview model is updated.
                      I'm still not able to prevent it from happening.

                      Here is the code I tried (but keep in mind that my listview is inside a Scrollview and I can't change this):

                          ScrollView{
                              id :view
                              anchors.fill: parent
                      
                              property int savedY : 0
                              flickableItem.flickableDirection: Flickable.VerticalFlick
                              flickableItem.boundsBehavior: Flickable.StopAtBounds
                              flickableItem.interactive: true
                      
                      
                              style: ScrollViewStyle {
                                  handle: Rectangle {
                                      implicitWidth: 10
                                      implicitHeight: 30
                                      radius: 5
                                      color: "grey"
                                  }
                                  scrollBarBackground: Rectangle {
                                      implicitWidth: 15
                                      color: "transparent"
                                  }
                                  decrementControl: Rectangle {
                                      implicitWidth: 0
                                      implicitHeight: 0
                      
                                  }
                                  incrementControl: Rectangle {
                                      implicitWidth: 0
                                      implicitHeight: 0
                                  }
                              }
                      
                              onYChanged: {
                                  y = savedY;
                              }
                      
                              ListView {
                                  id: dataView
                      
                      
                      
                                  clip: true
                                  spacing : 2
                                  model: window.model
                                  delegate: MyDelegate{}
                                  interactive: false
                      
                                  onModelChanged:{
                                      view.savedY = view.y;
                                  }
                      
                              }
                          }
                      
                      J.HilkJ 1 Reply Last reply
                      1
                      • D DavidM29

                        @J.Hilk
                        I tried to adapt your solution to my problem. But it does not work.
                        I'm not sure that the use of the Scrollview is compatible with your solution.

                        I don't know what cause the Scrollview to go all the way up when my listview model is updated.
                        I'm still not able to prevent it from happening.

                        Here is the code I tried (but keep in mind that my listview is inside a Scrollview and I can't change this):

                            ScrollView{
                                id :view
                                anchors.fill: parent
                        
                                property int savedY : 0
                                flickableItem.flickableDirection: Flickable.VerticalFlick
                                flickableItem.boundsBehavior: Flickable.StopAtBounds
                                flickableItem.interactive: true
                        
                        
                                style: ScrollViewStyle {
                                    handle: Rectangle {
                                        implicitWidth: 10
                                        implicitHeight: 30
                                        radius: 5
                                        color: "grey"
                                    }
                                    scrollBarBackground: Rectangle {
                                        implicitWidth: 15
                                        color: "transparent"
                                    }
                                    decrementControl: Rectangle {
                                        implicitWidth: 0
                                        implicitHeight: 0
                        
                                    }
                                    incrementControl: Rectangle {
                                        implicitWidth: 0
                                        implicitHeight: 0
                                    }
                                }
                        
                                onYChanged: {
                                    y = savedY;
                                }
                        
                                ListView {
                                    id: dataView
                        
                        
                        
                                    clip: true
                                    spacing : 2
                                    model: window.model
                                    delegate: MyDelegate{}
                                    interactive: false
                        
                                    onModelChanged:{
                                        view.savedY = view.y;
                                    }
                        
                                }
                            }
                        
                        J.HilkJ Offline
                        J.HilkJ Offline
                        J.Hilk
                        Moderators
                        wrote on last edited by
                        #12

                        hi @DavidM29

                        import QtQuick 2.12
                        import QtQuick.Controls 1.5
                        
                        ApplicationWindow {
                            id:root
                            visible:true
                            width:500; height:500
                        
                            Timer{
                                running: true
                                interval: 2000
                                repeat: true
                                onTriggered: view.changingModel++
                            }
                        
                            ScrollView{
                                    id :view
                                    anchors.fill: parent
                        
                                    property int savedY : 0
                                    flickableItem.flickableDirection: Flickable.VerticalFlick
                                    flickableItem.boundsBehavior: Flickable.StopAtBounds
                                    flickableItem.interactive: true
                        
                                    flickableItem.onContentYChanged: {
                                        console.log(flickableItem.contentY)
                                        if(flickableItem.contentY > 0)
                                            savedY = flickableItem.contentY
                                    }
                        
                                    property int changingModel: 5
                        
                        
                        
                                    ListView {
                                        id: dataView
                        
                        
                        
                                        clip: true
                                        spacing : 2
                                        model: view.changingModel
                                        delegate: Rectangle{color: "red"; width: dataView.width; height: dataView.height/4}
                                        interactive: false
                        
                                        onModelChanged:{
                                            view.flickableItem.contentY = view.savedY;
                                        }
                        
                                    }
                            }
                        }
                        

                        Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


                        Q: What's that?
                        A: It's blue light.
                        Q: What does it do?
                        A: It turns blue.

                        D 1 Reply Last reply
                        3
                        • J.HilkJ J.Hilk

                          hi @DavidM29

                          import QtQuick 2.12
                          import QtQuick.Controls 1.5
                          
                          ApplicationWindow {
                              id:root
                              visible:true
                              width:500; height:500
                          
                              Timer{
                                  running: true
                                  interval: 2000
                                  repeat: true
                                  onTriggered: view.changingModel++
                              }
                          
                              ScrollView{
                                      id :view
                                      anchors.fill: parent
                          
                                      property int savedY : 0
                                      flickableItem.flickableDirection: Flickable.VerticalFlick
                                      flickableItem.boundsBehavior: Flickable.StopAtBounds
                                      flickableItem.interactive: true
                          
                                      flickableItem.onContentYChanged: {
                                          console.log(flickableItem.contentY)
                                          if(flickableItem.contentY > 0)
                                              savedY = flickableItem.contentY
                                      }
                          
                                      property int changingModel: 5
                          
                          
                          
                                      ListView {
                                          id: dataView
                          
                          
                          
                                          clip: true
                                          spacing : 2
                                          model: view.changingModel
                                          delegate: Rectangle{color: "red"; width: dataView.width; height: dataView.height/4}
                                          interactive: false
                          
                                          onModelChanged:{
                                              view.flickableItem.contentY = view.savedY;
                                          }
                          
                                      }
                              }
                          }
                          
                          D Offline
                          D Offline
                          DavidM29
                          wrote on last edited by
                          #13

                          @J.Hilk
                          It works !
                          Thank you !
                          Did not know that I had to use the flickableItem.
                          Wish I could use some fresh Qt 5.12.

                          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