Dynamically create ListModel in QML
Asked Answered
E

4

11

When I need to create any QML component in runtime, I can use that guide: http://qt-project.org/doc/qt-5/qtqml-javascript-dynamicobjectcreation.html

i.e. just call Qt.createComponent and component.createObject

But I couldn't find how to create ListModel at runtime? with qml, not in c++.

You can ask, why I need it. So, I have a nested ListModel: there is outer model, which delegates contained inner models. So when I'm calling outer_model.append({}), I must pass newly created ListModel for inner model. I cannot use statically defined inner model in outer delegate, because I cannot access such model in runtime. By the way, can it be accessed somehow?

P.S. Maybe it's completely wrong idea to try managing models in javascript?

Exhalation answered 30/7, 2014 at 5:9 Comment(1)
There is a strong school of thought that says models should be managed in C++, while Javascript should be kept lightweight, really for managing the View only. In my experience, there's nothing wrong with managing models in Javascript, so long as your code is well structured. Qt doesn't provide a Javascript debugger, so you can't step through code or inspect variables. You have to rely on old school console.log() debug outputs, which makes it harder to troubleshoot or maintain code. Hopefully this will change in future versions of Qt Creator.Synthetic
D
8

Try this:

Component {
    id: someComponent
    ListModel {
    }
}

function createModel(parent) {
    var newModel = someComponent.createObject(parent);
    return newModel;
}
Dre answered 30/7, 2014 at 5:34 Comment(1)
Note that you can additionally add default values and child elements of the component in QML, so that JavaScript does not have to add them itself.Danish
L
14

I am a JS developer who writes QtQuick applications and this is something I have tried on with multiple solutions.

Short answer to managing models in JavaScript inside QML is that it's a nightmare. I would advice you to write a small sub-class of QAbstractListModel which internally uses QJsonArray as its data source, so that it makes it easier to understand the data structure in C++ as well as in its usage inside QML. Follow the instructions to create QML types from C++ here.

If you still want to do it inside JavaScript, another approach is the following:

function createNewList() {
    var newListModel = Qt.createQmlObject('import QtQuick 2.2; \
        ListModel {}', parent);
    return newListModel;
}

However this has some serious memory leak problems even after using gc()

If your primary concern is having ListModels inside ListModels, this following simple thing works for me (there is an implicit type conversion between array of objects and ListModels inside ListModels I think)

property ListModel items: ListModel {}

function addComplexItem() {
    items.append({
        "key": "People",
        "arr": [
            {
             "arrItemName": "John",
             "arrItemValue": 18,
            },
            {
             "arrItemName": "Kerry",
             "arrItemValue": 21,
            },
            {
             "arrItemName": "Mike",
             "arrItemValue": 19,
            }    
        ]});
}


// Usage
Component {
    id: viewDelegate

    Item {
        Text {
            text: "List of " + key
        }
        ListView {
            model: arr
            delegate: Rectangle {
                Text { 
                    text: arrItemName
                } 
            }
        }  
    }
}
Learned answered 2/8, 2014 at 18:25 Comment(2)
I like the JS solution you have here. My experience is that there's considerable coding overhead and a lot of restriction (tying down model structure) in going to QAbstractListModel route, so I prefer managing models in JS. However my issue is the memory leak in doing it this way. I recently reported this bug for Qt 5.5 which may be related. If so, hopefully the memory leak problem will be resolved by Qt 5.7.Synthetic
why we most don't use javascript in qtquick app? (for nightmare!!)Masson
D
8

Try this:

Component {
    id: someComponent
    ListModel {
    }
}

function createModel(parent) {
    var newModel = someComponent.createObject(parent);
    return newModel;
}
Dre answered 30/7, 2014 at 5:34 Comment(1)
Note that you can additionally add default values and child elements of the component in QML, so that JavaScript does not have to add them itself.Danish
C
0

Yet another approach:

ListModel {
    id: patterns
    property int _nb_elements: 2
    property int _nb_somearray: 3

    Component.onCompleted: {
        for (var p = 0; p < _nb_elements; p++) {
            var somearr = [];
            for (var s = 0; s < _nb_somearray; s++) {
                somearr.push({
                    "suvalue": "1"
                });
            }
            patterns.append({
                "somevalue": "A",
                "somearr": somearr // will be converted to ListElement
            });
        }
    }

}
Carib answered 1/4, 2022 at 10:31 Comment(0)
L
-1

I do the initialization like this:

dataObject_m1.initSystem= function() { // QML calls when ready 
  console.log( "ModulData_1.js func initSystem()");

  dataObject_m1.statisticsSystem= Qt.createQmlObject("import QtQuick 2.5; ListModel{}", dataObject_m1.parent, "dynamic_source" );
  dataObject_m1.statisticsSystem.objectName = "ModelSystem";
  dataObject_m1.statisticsSystem.append( { name: qsTr("System"), number: "", val: ""});
  dataObject_m1.statisticsSystem.append( { name: "S3500", number: "", val: ""});
  dataObject_m1.statisticsSystem.append( { name: "S3550", number: "", val: ""});
  dataObject_m1.statisticsSystem.append( { name: "S3551", number: "", val: ""});
  dataObject_m1.statisticsSystem.append( { name: "S9999", number: "", val: ""});
}    

And to update Data:

 var updateSystem = function ( numberMap , valMap) {
     console.log ("ModuleData_1.js: updateSystem" );

   for (var num in (numberMap )) {
      var j = dataObject_m1.idMap[num];
      dataObject_m1.statisticsSystem.set( j, { val : Map[num]});
      console.log ("number(" + numberMap[val]+ ") [" + String(j) + "]= numbeMap["+val+"]" )
  }
 for (var valx in (valMap)) {
     var k = dataObject_m1.idMap[valx];
     dataObject_m1.statisticsSystem.set( k, { bad : valMap[valx]});
     console.log ("val(" + valMap[valx]+ ") [" + String(k) + "]= valMap["+valx+"]" )
  }
}

Access fuction to model:

 var statisticsModelSystem= function()  {
    console.log ("ModulData_1.js: get statisticsModelSystem" );
    if ( typeof(dataObject_m1.statisticsSystem) !== 'object')
        console.error(" statisticsSystem is undefined ")

    return dataObject_m1.statisticsSystem;
}
Leighton answered 13/4, 2018 at 15:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.