How can I get ListView to create/destroy items less predictive



  • Hi all,
    the ListView component destroys all items and creates new ones even when I just change the number of items in the model. It reuses the old ones when the number of items don't change although the model changes. Here's an example:

    property var _model: [
    	[
    		{ t: "A0" },
    		{ t: "B0" },
    		{ t: "C0" },
    		{ t: "D0" },
    	],
    	[
    		{ t: "A1" },
    		{ t: "B1" },
    		{ t: "C1" },
    		{ t: "D1" },
    	],
    	[
    		{ t: "A2" },
    		{ t: "B2" },
    		{ t: "C2" },
    		{ t: "D2" },
    		{ t: "E2" },
    	]
    ]
    
    property int _index: 0
    
    ColumnLayout {
    	anchors.fill: parent
    	ListView {
    		id: listView
    		Layout.fillHeight: true
    		Layout.fillWidth: true
    		model: _model[_index].length
    		delegate: Text {
    			text: _model[_index][index].t
    			Component.onCompleted: console.log("c ", index)
    			Component.onDestruction: console.log("d ", index)
    		}
    	}
    	RowLayout {
    		Repeater {
    			model: 3
    			Button {
    				Layout.fillWidth: true
    				text: index
    				onClicked: _index = index
    			}
    		}
    	}
    }
    

    When I click the second button labeled "1", no Text component will be destroyed or created, only the contents are updated. When I click the third button labeled "2", all Text components will be destroyed and new ones created. Is there a way to prevent ListView from being so "smart"? It should only destroy/create items when necessary or it get's told so. I would expect it knows which items at which index it displays and just adds one item when the number of items changes from 4 to 5.

    Cheers!



  • Hi all, the ListView component destroys all items and creates new ones even when I just change the number of items in the model.

    A JS array does not provide notifications that an item has been inserted, removed, or modified. What happens under the hood is that the ListView gets simply assigned a new JS array, so it rebuilds the entire view after being assigned a new model. JS arrays are handy for static models, but for dynamic changes it is strongly advised to use a proper model that provides fine-grained change notifications. For example, take a look at QML ListModel if you prefer to manage the model in QML. If you'd like to manage the model in C++, any QAbstractItemModel subclass would do.



  • We are using the special models from http://gitlab.unique-conception.org/qt-qml-tricks . To us they have been very useful, as they get rid of this painful behavior of re-creating the entire list all the time. However we do create these models from c++, and then use them in repeaters, so I'm not sure if this is useful for you.

    Cheers!



  • @jpnurmi Thanks for your reply! I'm not sure if the ListView gets a new JS array assign, cause the model of ListView is not the array itself, I used just the length of the array. So in my understanding the ListView shouldn't know anything about the model the delegate uses.
    The model in my code snippet is just an example, my model actually consists of nested JS arrays and objects. As far as I understood nested QML ListModels are not possible, aren't they?



  • @Zicandar Thanks for your reply! I'll have a look at these models. But the model in my code snippet is just an example. Actually I have nested JS arrays and objects. Is that possible with the models you proposed?



  • Right, I missed that you weren't using a JS array directly but its length as the model. Nevertheless, whether you use a JS array or its length makes no difference. When you change the model from A to B, the view is rebuilt. If you change data within a proper model that provides change notifications, only necessary changes are applied.



  • @jpnurmi Ok, thanks, I'm gonna think about creating my model in C++. As far as I learned there's no model which supports nesting in qml.



  • @DuBu Yes, these models support nesting, however as far as I know they still require C++ to be used.



  • @jpnurmi I spent a couple of days figuring out how to create a model based on QAbstractItemModel and pass it to a ListView like I did with my example JS model. I used a QAbstractItemModel instead of QAbstractListModel cause it seems to have a better support for nested models. I then tested it with a TreeView and it worked well. But I still don't know how to use it with a ListView the way I do it in my example. In my example my goal is to have 3 sub models which I can pass one by one to a ListView. Basically I want to change the model of the ListView by user interaction.
    Does a QAbstract...Model help my ListView in that case not to remove all delegates and create the same ones again or am I on the completely wrong path?



  • @Zicandar I had a look at the 2 models you proposed, QQmlObjectListModel and QQmlVariantListModel. Unfortunately they've got no example QML code to show how they are used. Do you have a QML code snippet showing the use of one of these models? Perhaps event related to my example?


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.