Dynamic generation of ListView content



  • Hi all,

    I've got a QML application and I'm trying to fill in a ListView with data pulled from a SQL database (in C++). Getting data works fine, but creating a ListView with an arbitrary number of entries is being problematic. I've been searching the web for this and have seen a lot of ways of doing this, which are variably complicated and weird. What I settled on is this:

    import QtQuick 2.7
    import QtQuick.Controls 2.2
    import QtQml 2.2
    import QtQml.Models 2.3
    
    Item
    {
        z: 3
        visible: false
    
        height: 680
        width: 800
    
        property int iRowCount: 7
        property int ixRowCounter: 1
    
        Text
        {
            id: lblTitle
            anchors.top: parent.top
            anchors.topMargin: 10
            anchors.left: parent.left
            anchors.leftMargin: 10
            text: "Choose flavors:"
            font.pointSize: 20
            horizontalAlignment: Text.AlignLeft
        }
    
        Rectangle
        {
            id: yoMenuContainer
            anchors.left: parent.left
            anchors.leftMargin: 10
            anchors.right: parent.right
            anchors.rightMargin: 10
            anchors.top: lblTitle.bottom
            anchors.topMargin: 5
            border.width: 1
            height: 400
    
            ListModel
            {
                id: yoListModel
                ListElement
                {
                    sqlID: 0
                }
            }
    
            ListView
            {
                id: yoListView
                anchors.fill: parent
                model: yoListModel
    
                delegate: yoDelegate
                clip: true
            }
    
            Component
            {
                id: yoDelegate
    
                Item
                {
                    id: yoItem
    
                    Component.onCompleted:
                    {
                        var component = Qt.createComponent("YoMenuRow.qml");
    
                        if(ixRowCounter <= iRowCount)
                        {
                            yoListModel.append({"sqlID": ixRowCounter});
                            if(component.status === Component.Ready)
                            {
                                var object = component.createObject(yoItem, {"id": "yoRow"+ixRowCounter});
                            }
                            object.y = object.height * (ixRowCounter - 1);
                            ixRowCounter++;
                        }
                    }
                }
            }
        }
    }
    

    ... the referenced YoMenuRow.qml file:

    import QtQuick 2.7
    import QtQuick.Controls 2.2
    
    Row
    {
        id: yoRow
        spacing: 30
        height: 100
        bottomPadding: 5
        CheckBox
        {
            anchors.verticalCenter: parent.verticalCenter
            onCheckStateChanged:
            {
                console.log("Checked box for id " + sqlID);
            }
        }
        Image
        {
            source: "/img/img-entry-64.png"
        }
        Text
        {
            text: "This entry"
            anchors.verticalCenter: parent.verticalCenter
            font.pointSize: 20
        }
    }
    

    ... as you can see, I'm not even attempting the SQL stuff at this point, I just want a ListView with an arbitrary number of lines generated when the page loads. This code does that just fine, and I have some placeholder code that loops through and creates some number of entries (iRowCount, currently 7). I get 7 entries, BUT the flickable part is broken.

    The behavior I get now is that when the screen appears, I have all 7 entries. If I pull DOWN on the list (so beyond the starting point), it behaves fine - it snaps back when I let go. If I pull UP, to see entries farther down, the entire list disappears. It behaves a bit like it's trying to scroll, but at light speed so the entire list snaps out of view instantly. If I let go, it snaps back to the first list item.

    I've done various things that have changed this behavior slightly, but never to a useful degree. If I remove the Component.onCompleted stuff and just put in 7 Rows, the Flickable part works fine, so in theory the basic setup of the ListView is right, just the Component part is breaking it.

    Any suggestions on what I can try here? I get the feeling that when I'm creating components, something - a size, or index, or position - is just getting off, and if I fix that I'll be good. That's my hope, anyway!

    Thanks!



  • @Elliott Impossible to test (pasted to two qml files):

    qrc:/main.qml:55: ReferenceError: ixRowCounter is not defined
    qrc:/main.qml:22: ReferenceError: tStyle is not defined
    qrc:/main.qml:19: ReferenceError: tStyle is not defined
    qrc:/main.qml:17: ReferenceError: lblTitle is not defined
    

    Style can be changed ad hoc but please at least make the example self-contained with ixRowCounter and other possible crucial things.



  • @Eeli-K Whoops! Sorry, I was trying to cut out unnecessary stuff but got carried away. I edited the original post so the code should be more self-contained.



  • @Elliott I just can't understand either what you want to do or why you're doing it that way. To create delegate objects you don't need Component.onCompleted of the delegate or createObject/createComponent. Especially you shouldn't care about creating delegates and positioning inside the delegate that way. It's the list view which creates and positions delegate objects. You handle only the model.

    Here's a simplified app:

    import QtQuick 2.6
    import QtQuick.Controls 2.2
    
    ApplicationWindow {
        visible: true
        width: 640
        height: 480
    
        Item
        {
            anchors.fill:parent
            id: root
    
            property int iRowCount: 7
    
            Rectangle
            {
                id: yoMenuContainer
                anchors.left: parent.left
                anchors.leftMargin: 10
                anchors.right: parent.right
                anchors.rightMargin: 10
                anchors.top: lblTitle.bottom
                anchors.topMargin: 5
                border.width: 1
                height: 400
    
                //model is handled here for convenience, finally it would be in c++
                // and triggered by whatever is suitable for the app
                Component.onCompleted:
                {
                    // iRowCount is in the root item but for testing could be hardcoded here as well
                    for (var rowCounter = 0; rowCounter < root.iRowCount; rowCounter++) {
                        yoListModel.append({"sqlID": rowCounter});
                    }
                }
    
                ListModel
                {
                    id: yoListModel
                }
    
                ListView
                {
                    id: yoListView
                    anchors.fill: parent
                    model: yoListModel
    
                    delegate: YoMenuRow{} //No createComponent! No positioning!
                    clip: true
                }
            }
        }
    }
    


  • @Eeli-K That is perfect! The code I had was getting progressively more complex and weird because what I started with wasn't working, and I was trying progressively more desperate things. Having object.y = object.height * (ixRowCounter - 1); in particular seemed like a warning sign that things had gotten out of hand. This code is working exactly like I needed. Thanks!


Log in to reply
 

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