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.8k 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.
  • 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