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

ListView : delay between onRowsRemoved and actual deletion of the delegate



  • Hi !

    I am using a ListView that displays a model defined in C++ based on a QAbstractListModel.

    Each delegate of the ListView contains a delete button to delete the item in the model on which the delegate is based.

    ListView {
        Connections {
            target: projects
            onRowsRemoved: {
                console.log('the row has been removed in the model')
            }
        }
    
        model: projects
    
        delegate: Item {
            // stuff
    
            Component.onDestruction: {
                console.log('component destroyed')
            }
    
            Button {
                text: 'Delete the ' + project.name + ' project' // each item in the projects model is accessible through 'project'
                onClicked: {
                    projects.remove(project) // remove the current item from the model
                }
            }
        }
    }    
    

    In C++, the method remove(Project *project); is what is called by QML and looks like this :

    void ProjectsModel::remove(Project *project) {
        size_t index = getIndexInModel(project); // returns the index of project in the list
    
        beginRemoveRows(QModelIndex(), index, index);
    
        delete _projects.at(index);
        _projects.removeAt(index);
    
        endRemoveRows();
    }
    

    When I run my program and delete a row by clicking on the button, I get the following output :

    qml: the row has been removed in the model
    qrc:xxx.qml: TypeError: Cannot read property 'name' of undefined
    qml: component destroyed
    

    It seems that there is a delay between the row being removed in the model and the actual removal of the delegate that represent that row. This delay is long enough that it allows the bindings to be processed and the delegate to try to access the content of the item that has already been deleted.

    Since the ViewList aknowledge that the row has been removed (as can be seen with the message logged when the rowsRemoved is received), but do not delete the delegate right away.

    Am I missing something ? I do not really understand why it is possible for a deleted row to still have its delegates bindings processed.

    Any ideas on how to fix this problem ?

    Thanks !


  • Moderators

    @ebatsin
    my first guess would be garbage collection.
    Also just to make sure: You haven't set the ListView.delayRemove attached proeprty right?

    Also where does project come from exactly? How is it instantiated?

    Any ideas on how to fix this problem ?

    see the attached properties/signals of ListView element

    For example, maybe the ListView.remove() signal is emitted earlier than Component.onCompleted.



  • ListView.delayRemoved is false

    My project list is simply a QList<Project*> _projects;

    To append to it I have the following method :

    void ProjectsModel::append(Project* project) {
        int index = _projects.count();
        
        beginInsertRows(QModelIndex(), index, index);
    
        _projects.insert(index, project);
    
        endInsertRows();
    }
    

    And to access project in QML, my ProjectsModel defines the role project and returns it like this :

    QVariant ProjectsModel::data(QModelIndex const& index, int role = Qt::UserRole) const override {
        // boundary checks
    
        // I do not check the role since I defined only one user role & I always want to return the whole project.
        return QVariant::fromValue(_projects[index.row()]);
    }
    

    I'll look into the order in which the signals are emitted


Log in to reply