Removing multiple rows from TableView



  • Hi,

    I am trying to remove multiple rows from a TableView. I have a selection and on deletion action traverse the selected rows with

    tableView.selection.forEach(function(rowIndex) {myModel.deleteRow(rowIndex)})
    

    Now what happens is that the first selected row is deleted and the view is updated. Then the next row is deleted but since one line has already been deleted, all the row indexes below are now shifted by 1.

    How is the approach here in general?

    • Find a way to not update the view between the deletions?
    • Recalculate the index after every deletion?

    Thanks for any hints.


  • Lifetime Qt Champion

    Hi,

    There are usually three ways:

    1. Do it in reverse order
    2. Keep deleting the starting row for the number of rows to remove.
    3. If your model is base on QAbstractItemModel, call removeRows on it with the start index and the count

    Hope it helps



  • Thank you for your answer.

    I forgot to mention that the selection is not contiguous.
    So I guess it's 1. then. I will try that out later today.



  • OK, so far so bad. :)
    Since I am using a QSortFilterProxyModel, I cannot make any assumptions about the "real" rowIndex position. So I cannot tell which row is the "last" one that should be removed first.

    I have now added a unique ID to my data source and will try to identify the rows to remove that way. Will post my solution soon'ish. :)



  • function removeSelectedRows() {
        for (var i = selection.__ranges.length - 1 ; i >= 0 ; -- i) {
            var index = selection.__ranges[i][0];
            var count = 1 + selection.__ranges[i][1] - selection.__ranges[i][0];
            model.remove(index, count)
        }
        
        selection.clear()
    }
    

    This worked for me, but I'm not using a QSortFilterProxyModel.



  • Hi,

    looks tempting (though not with QSqortFilterProxyModel) but for my understanding breaks with two "best practices" of the QML world.

    1. limit the use of JavaScript to UI logic and put your business logic into the C++ backend
    2. avoid double-underscore __methods because they are private and can change with any minor version of Qt

    My approach will be to fill an array with all the selected rows on the QML side, hand that to my ProxyModel (C++), remap the rowIndexes there to the ones of my SqlTableModel, hand that new list to that model and then try to remove all rows in one transaction with QSqlQuery::execBatch. Not sure if I succeed (still a beginner) but this feels like a good approach.

    Regards



  • So here is what works for me.
    Please comment if you think there are things to improve. :)
    What I do is:

    • View: create a list of selected rows and give that list to the ProxyModel
    • ProxyModel: map the proxy row indexes to the table row indexes and give the new list to the SqlTableModel
    • SqlTableModel: remove the rows in reverse order to not shift the remaining row indexes in the list

    The QML...

    Menu {
        id: contextMenu
        MenuItem {
            text: qsTr("Delete")
            shortcut: "Del"
            onTriggered: {
                var selectedRows = []
                tableView.selection.forEach(function(rowIndex) {selectedRows.push(rowIndex)})
                myModel.deleteRows(selectedRows)
                tableView.selection.clear()
                tableView.selection.select(tableView.currentRow)
            }
        }
    }
    

    ...the ProxyModel...

    void ProxyModel::deleteRows(QVariantList selectedRows)
    {
        QVector<int> rowsToDelete;
        for (QVariant row : selectedRows) {
            QModelIndex proxyIdx = index(row.toInt(), 0);
            QModelIndex sourceIdx = mapToSource(proxyIdx);
            rowsToDelete.append(sourceIdx.row());
        }
        qobject_cast<SqlTableModel*>(sourceModel())->deleteRows(rowsToDelete);
    }
    

    ...and the ugly ... err, the SqlTableModel

    void SqlTableModel::deleteRows(QVector<int> rows)
    {
        bool success;
        for (auto it = rows.crbegin(); it != rows.crend(); ++it) {
            int row = *it;
            beginRemoveRows(QModelIndex(), row, row);
            success = removeRow(row);
            if (success == true) {
                endRemoveRows();
            }
        }
    }
    

Log in to reply
 

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