Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Gridview dynamic update on C++ model setData() or qml ListModel set()



  • Hi all,

    I'm having problems understanding why this is not working, I just want to have an item in the view dynamically updated when I change the data in the model using the ListModel method set() , I've also used a C++ model and setData() which emits dataChanged() signal but I can't see any refresh though. I'm stucked.

    import QtQuick 2.5
    import QtQuick.Controls 2.2
    import QtQuick.Layouts 1.2
    
    
    Dialog {
        id: mydialog
        anchors.centerIn: parent
        width: parent.width / 2
        height: parent.height / 2
        padding: 32
        modal: true
    
        ListModel {
            id: theModel
    
            ListElement { number: 0 }
            ListElement { number: 1 }
            ListElement { number: 2 }
            ListElement { number: 3 }
            ListElement { number: 4 }
            ListElement { number: 5 }
            ListElement { number: 6 }
            ListElement { number: 7 }
            ListElement { number: 8 }
            ListElement { number: 9 }
        }
    
        ColumnLayout {
            anchors.fill: parent
    
            property int count: 9
    
            Button {
                Layout.alignment: Qt.AlignTop
                Layout.preferredHeight: 20
                Layout.preferredWidth : 50
    
                text: qsTr("add")
    
                onClicked: {
                    console.debug("add element")
                    theModel.append({"number": ++parent.count});
                }
    
            }
    
            Button {
                Layout.alignment: Qt.AlignCenter
                Layout.preferredHeight: 20
                Layout.preferredWidth : 50
    
                text: qsTr("set")
    
                onClicked: {
                    console.debug("set")
                    theModel.set(0, {"number": 12});
                }
    
            }
    
            GridView {
                Layout.alignment: Qt.AlignBottom
                id: view
                Layout.preferredHeight: 100
                Layout.preferredWidth : 400
                //anchors.margins: 20
    
                clip: true
    
                model: theModel
    
                cellWidth: 45
                cellHeight: 45
    
                delegate: numberDelegate
            }
    
            Component {
                id: numberDelegate
    
                Label {
                    width: 400
                    height: 40
                    text: index
                }
            }
    
        }
    }
    
    

    follows a second attempt which is more my real case ( I have a custom C++ model ) registered in My.Models 1.0 -> MyModel . It's bit better as I'm able to change the values in the view but the view items are not refreshed until I scroll up or down. Using grid.forceLayout() didn't solve, the only solution I've found is to set grid.model = 0 and reset the model with grid.model = mymodel . I expect this to work without any special trick. here is the code ( I've stripped it lot of lines so maybe the syntax is not correct, hope it's enough ):

    import QtQuick 2.12
    import QtQuick.Controls 2.5
    import QtQuick.Layouts 1.3
    import QtQuick.Controls.Material 2.3
    
    import My.Models 1.0
    
    Dialog {
        id: root
    
        anchors.centerIn: parent
        width: 1000
        height: 700
        padding: 32
        modal: true
    
        Connections {
            target: MyClass
            // MyClass emit this signal
            onFinishedParsing: {
                // this is an example trying to set all model data to "0.2" , in a real case this data come from MyClass
                for (var i = 0; i < mymodel.rowCount(); i++) {
                    mymodel.setData(mymodel.index(i, 0),"0.2",MyModelModel.Value)
                    }
        }
        
        ColumnLayout {
            anchors.fill: parent
            spacing: 16
    
           GridView {
                id: grid
                Layout.fillHeight: true
                Layout.fillWidth: true
                contentWidth: width
                cellWidth: width / 4
                cellHeight: 80
                clip: true
    
                model: MyModel {
                    id: mymodel
                    }
    
                delegate: Label {
                    text: model.value
                }
            }
    }
    

    the setData method in MyModel is:

    bool SimplsModel::setData(const QModelIndex &index, const QVariant &value,
                              int role) {
      if (index.isValid() && role == SimplsModel::Roles::Value) {
        m_rowData[index.row()].value = value.toFloat();
        emit dataChanged(index, index, {SimplsModel::Roles::Value});
        return true;
      }
      return false;
    }
    

    I'm really stucked , can't see what's wrong. any help would be really appreciated. thanks.



  • Here is my code which use the QAbstractListModel: TestModel

    When change the data, try call beginResetModel and endResetModel.

    void TestModel::addDao(const QString &name, int age)
    {
        beginResetModel();
        m_datas.append(Dao(name, age));
        endResetModel();
    }
    


  • When change the data, try call beginResetModel and endResetModel.

    Don't (I wouldn't anyhow) do that, that's a big hammer. I do use that but only for a whole set of data.
    For an simple append ~not really right.

    setData & emit dataChanged is correct.

    When reimplementing the setData() function, this signal must be emitted explicitly.

    I emit that the whole row changed with mine (see the first 2 args below), but I'm not sure even this is fully optimal. It does work for me - just working is enough for me atm and I'd like to get time to experiment restricting to a unique.

      emit QAbstractTableModel::dataChanged(QAbstractTableModel::index(intOfIter, 0), QAbstractTableModel::index(intOfIter, columnCount()-1), QVector<int>() << role);
    

    I'm still getting my head around trying to get setData actually working from QML side with custom roles, from Table view but this works from Cpp backend, I'm trying to get a complex control sorted that can handle updates with dynamic delegates from QML.

    Hopefully this is just enough to get you across the line and moving again too.



  • @qyvlik

    beginResetModel() / endResetModel()
    

    works but actually is a lot of overhead in in setData, I think it's ok if I want to upadate the whole model data in one shot , thanks

    @6thC

    I've tried this but it didn't solve, as before it do set the data but the view doesn't refresh de delagates, maybe I didn't realize how to apply it for a QAbstractListModel:

    emit QAbstractListModel::dataChanged(QAbstractListModel::index(index.row()), QAbstractListModel::index(index.row()), QVector<int>() << SimplsModel::Roles::Value); 
    
    or 
    
    emit QAbstractListModel::dataChanged(QAbstractListModel::index(index.row()), QAbstractListModel::index(index.row()), QVector<int>() << Qt::DisplayRole);
    

    Strange that we have to fight to make this work, I can't find code examples about people achieving this, Am I doing something unusual ? ( I wander )

    thanks


Log in to reply