Unsolved How do I form a Listview containing mixed items
-
I'm new to QML and have been struggling with building a ListView from data of varying content. I have a UI requirement to display content of varying type, ie. where the delegate changes depending on the type of the item. Now the actual data to display comes from a C++ "node" list (QAbstractItemModel). Some of the nodes have peripheral devices. The desire is to display both the nodes and any peripherals in a flat list with a custom sort.
I've managed to cook up a solution that involves inspecting the node list using onCompleted handlers, and creating lightweight javascript objects that hold the underlying data (be that a node or a peripheral). These "proxy" model items are dynamically added to a ListModel property on the view with the (node / peripheral) delegate being selected by a Loader.
The whole thing feels wrong, and importantly does not handle changes to the underlying C++ model (eg. firmware update flag).
So the question is how do I create a proxy model for a ListView where the model items are not of the same type, eg. where the model items are a mixture of nodes and peripherals?
========================
QML code snippet of what I currently have follows
ListView {
id : listView property ListModel nodesModel: ListModel {} property var nodeInfo : [] Repeater { // <<========== Purely here to provide an imperative-like hook to process the underlying mode model: nodesData.nodesModel // <<============ C++ QAbstractItemModel delegate: Item { Component.onCompleted: { listView.nodeInfo.push({ "section": hwVariant < 200 ? "chair" : "tools", "heading": moduleFamily, "hwVariant" : hwVariant, "rowType": "system_node", // <<============ Used to dynamically choose delegate "itemModel" : model, // <<=========== C++ individual nodes complete with notifyable QPROPERTYs . Yes they work in a simple context }); // Add a peripheral if this node has an associated function key if (model.functionKeyInfo.hasOwnProperty("required") && model.functionKeyInfo.required) { listView.nodeInfo.push({ "section" : "chair", "heading" : model.functionKeyInfo.heading, "rowType" : "function_key", "itemModel" : model.functionKeyInfo, }); } } } Component.onCompleted: { // sort nodesInfo by section, then rowType, then hwVariant, then serial number listView.nodeInfo.sort(Helpers.fieldSorter([ {field : 'section', order : ["chair", "tools"]}, {field : 'rowType', order : ['system_node', 'function_key']}, 'hwVariant', 'serial' ])); listView.nodeInfo.forEach( function (ni) { // translate fields then append to the listView model ni.section = qsTrId("gui.systemSummary." + ni.section); listView.nodesModel.append(ni); }); } }
// Actual delegates down here...
// The "system_node" rowTypes use a delegate that queries the C++ model directly
// The "function_key" rowTypes use a delegate that queries the peripheral model}
-
I think you should:
- make your Model more flexible
- make a delegate which adjusts automatically
- remove all that unnecessary logic from QML, move as much to C++ as possible
How to go about it?
- define common API for both peripheral and node model
- use that common API to create single C++ ItemModel which you will pass to QML
- make a Delegate which will change it's look depending on values returned by the API. Use a Loader if you must (to load either NodeDelegate or PeripheralDelegate), or if possible use single component which adjusts dynamically
Sorry for being rather vague, but I don't now your system.