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.
  • D Offline
    D Offline
    DavidM29
    wrote on last edited by
    #1

    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 1 Reply Last reply
    0
    • 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 Online
        J.HilkJ Online
        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 Online
          JonBJ Online
          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 Online
            J.HilkJ Online
            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 Online
                      J.HilkJ Online
                      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 Online
                          J.HilkJ Online
                          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