Search code examples
qtlistviewqmllistmodel

Qt QML Why isn't this ListView and ListModel consitent?


I'm having a problem with a ListView and a simple ListModel;

import QtQuick 2.12
import QtQuick.Controls 2.12

ApplicationWindow
{
    id: app
    visible: true
    width: 640
    height: 480
    property int bheight: 48

    ListModel
    {
        id: jmodel
    }

    function dosomething1()
    {
        jmodel.clear();
        jmodel.append({"atext":"hello"})
    }

    function dosomething2()
    {
        jmodel.clear();
        jmodel.append({"atext":"hello","btext":"world"});
    }

    ListView
    {
        id: alist
        width: parent.width
        height: parent.height - bheight
        model: jmodel
        delegate: Item
        {
            width: app.width
            height: 48
            Row
            {
                spacing: 8
                Text
                {
                    text: model.atext || ""
                }

                Text
                {
                    text: model.btext || ""
                }
            }
        }
    }

    Row
    {
        height: bheight
        anchors.top: alist.bottom
        Button
        {
            // press "plan A" first and it will say "hello"
            // press "plan B" after and it will remain "hello"
            // pressing A & B will NOT toggle
            text: "Plan A"
            onClicked: dosomething1();
        }

        Button
        {
            // press "plan B" first and it will say "hello world"
            // press "plan A" after and it will say "hello"
            // pressing A & B will toggle
            text: "Plan B"
            onClicked: dosomething2();
        }
    }

}

I can't get it to work consistently. Maybe there's something simple missing or i do not understand.

Pressing "Plan A" will say "hello" and after pressing "Plan B" will not change it.

But,

Pressing "Plan B" first will say "hello world", and thereafter pressing "plan A" and "plan B" will toggle between "hello world" and "hello".

It should not depend on what button is pressed first, i am clearing the model each time.

I have tried this on Qt5.12.3 and 5.9 and there is no change.

update

Following @eyllanesc answer, i've updated my code to instead create a new model for each update. This works but i don't know if i now have a memory leak.

this is my code:

import QtQuick 2.12
import QtQuick.Controls 2.12

ApplicationWindow
{
    id: app
    visible: true
    width: 640
    height: 480
    property int bheight: 48

    Component
    {
        id: jmodel
        ListModel {}
    }

    function dosomething1()
    {
        var m = jmodel.createObject()
        m.append({"atext":"hello"})
        alist.model = m;
    }

    function dosomething2()
    {
        var m = jmodel.createObject()
        m.append({"atext":"hello","btext":"world"});
        alist.model = m;
    }

    ListView
    {
        id: alist
        width: parent.width
        height: parent.height - bheight
        delegate: Item
        {
            width: app.width
            height: 48
            Row
            {
                spacing: 8
                Text
                {
                    text: model.atext || ""
                }

                Text
                {
                    text: model.btext || ""
                }
            }
        }
    }

    Row
    {
        height: bheight
        anchors.top: alist.bottom
        Button
        {
            text: "Plan A"
            onClicked: dosomething1();
        }

        Button
        {
            text: "Plan B"
            onClicked: dosomething2();
        }
    }



}


Solution

  • As the docs point out:

    Modifying List Models

    Modifying List Models The content of a ListModel may be created and modified using the clear(), append(), set(), insert() and setProperty() methods. For example:

    Component {
        id: fruitDelegate
        Item {
            width: 200; height: 50
            Text { text: name }
            Text { text: '$' + cost; anchors.right: parent.right }
    
            // Double the price when clicked.
            MouseArea {
                anchors.fill: parent
                onClicked: fruitModel.setProperty(index, "cost", cost * 2)
            }
        }
    } 
    

    Note that when creating content dynamically the set of available properties cannot be changed once set. Whatever properties are first added to the model are the only permitted properties in the model.

    (emphasis added)

    You can not add or remove the properties after initially establishing them:

    • In case 1 your model only has the atext property initially, and then you want to add the btext property but Qt does not allow it.

    • In case 2 your model has the properties atext and btext, then you just rewrite the atext property so btext still exists and will have a value of null.

    The solution in this case is to set btext with a default value in case 1:

    function dosomething1()
    {
        jmodel.clear();
        jmodel.append({"atext":"hello", "btext":""}) // <----
    }