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