Remove and insertrow for Martin Fitzpatricks example

Hello, how would remove and insertrow look like for Martin Fitzpatricks example above?

First one needs def removeRows()/def InserRows() for the QAbstractTableModel class and also some behaviour inside the QMainwindow class

    # This I have for my QAbstractTabelModel class
    def insertRows(self, position, rows, QModelIndex, parent):
        self.beginInsertRows(QModelIndex(), position, position+rows-1)
        for i in range(rows):
            default_row = ['']*len(self._headers)
            self_data.insert(position, default_row)
        self.endInsertRows()
        return true


    def removeRows(self, position, rows, QModelIndex, parent):
        self.beginRemoveRows(QModelIndex(), position, position+rows-1)
        for i in range(rows):
            del(self._data[position])
        self.endRemoveRows()
        return true

Not sure if that is correct, Its what I’ve pieced together from examples on other sites.

For the QMainwindow class I’m not relay sure how to proceed, the examples on other sites are not very clear to understand.

Some structured extra explanation on this subject for Martins example of this would be helpful.

The row of data should be removed for both the model and the view.

Not sure if this is what your looking for but this is what I have working for my sqlite tableview setup.

def initializedModel(self):
    self.model.setTable("commands")
    # self.model.setEditStrategy(QSqlTableModel.OnFieldChange)
    self.model.setEditStrategy(QSqlTableModel.OnManualSubmit)
    self.model.select()
    self.model.setHeaderData(0, Qt.Horizontal, "ID")
    self.model.setHeaderData(1, Qt.Horizontal, "Category")
    self.model.setHeaderData(2, Qt.Horizontal, "command_alias")
    self.model.setHeaderData(3, Qt.Horizontal, "command")
    self.model.setHeaderData(4, Qt.Horizontal, "requires")
    self.model.setHeaderData(5, Qt.Horizontal, "description")
    self.model.setHeaderData(6, Qt.Horizontal, "controlpanel")
    self.model.setHeaderData(7, Qt.Horizontal, "verification")

def onAddRow(self):
    self.model.insertRows(self.model.rowCount(), 1)
    self.model.submit()

def onDeleteRow(self):
    self.model.removeRow(self.tableView.currentIndex().row())
    self.model.submit()
    self.model.select()

def closeEvent(self, event):
    db.close()

To clear all the rows or content you can do something like the below.

self.w_tablewidget.clearContents()
self.w_tablewidget.setRowCount(0)

This is how I have set up my QTableView(not subclassed) and QAbstractTableModel (subclassed as TableModel):

    self.model = TableModel(data)
    self.proxyModel = QSortFilterProxyModel()
    self.proxyModel.setSourceModel(self.model)
    self.table_clients.setSortingEnabled(True)
    self.table_clients.setModel(self.proxyModel)
    self.selectionModel = self.table_clients.selectionModel()
    self.table_clients.setSelectionBehavior(QTableView.SelectRows)

I for the QAbstractTableModel class I know have this:

def removeRows(self, position, rows, parent):
    print("passed def removingrows")
    #print "\n\t\t ...removeRows() Starting position: '%s'"%position, 'with the total rows to be deleted: ', rows
    self.beginRemoveRows(parent or QModelIndex(), position, position + rows - 1)
    print("passed beginremovingrows")
    for i in range(rows):
        del(self._data[position])
        print("passed deleted row")
    self.endRemoveRows()
    print("passed endRemoveRows")
    return true

For the Mainwindow class I have this:

def removerow(self):
    #self.table_clients.model().layoutAboutToBeChanged.emit()
    print("passed def removerow")
    proxy_index = self.table_clients.selectedIndexes()[0]
    print("passed proxyindex = " + str(proxy_index))
    #convert
    source_index = self.proxyModel.mapToSource(proxy_index)
    print("passed sorceindex = " + str(source_index))
    #delete row in source model
    self.model.removeRow(source_index.row())
    # ---- --- something is wrong after this passage - it crashes here ----
    print("passed modelremoveRow source index")
    #self.model.layoutChanged.emit()
    self.table_clients.clearSelection()

It crahes after self.model.removeRow(source:index.row()) because the promt prints this:
= RESTART: C:\Users\svart\Documents\Python\Pythonprojects\AIXCRM\PRM_AIX_200629.py
passed def removerow
passed proxyindex = <PyQt5.QtCore.QModelIndex object at 0x0000021FDA289EB0>
passed sorceindex = <PyQt5.QtCore.QModelIndex object at 0x0000021FDA289F20>
passed def removingrows
passed beginremovingrows
passed deleted row
passed endRemoveRows

This might be something basic thing that is escaping me?
And it doesnt change anything if I remove the # from the lines with layoutAboutToBeChanged/layoutChanged, it still crashes at the same place.

And thank you Mike2750 for the reply but I couldnt make anthing of that advice, partially I guess becouse my program is setup a bit different.

Hi @David_Hansson welcome to the forum! I’ve written up a small example with two actions which trigger add/remove of rows on the table model. One important thing is to notify the view that the layout of the data has changed after doing this, otherwise it won’t update.

import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt


class TableModel(QtCore.QAbstractTableModel):

    def __init__(self, data):
        super(TableModel, self).__init__()
        self._data = data

    def data(self, index, role):
        if role == Qt.DisplayRole:
            value = self._data[index.row()][index.column()]
            return str(value)

    def rowCount(self, index):
        return len(self._data)

    def columnCount(self, index):
        return len(self._data[0])
                
    def insertRows(self, position, rows, QModelIndex, parent):
        self.beginInsertRows(QModelIndex, position, position+rows-1)
        default_row = ['']*len(self._data[0])  # or _headers if you have that defined.
        for i in range(rows):
            self._data.insert(position, default_row)
        self.endInsertRows()
        self.layoutChanged.emit()
        return True

    def removeRows(self, position, rows, QModelIndex):
        self.beginRemoveRows(QModelIndex, position, position+rows-1)
        for i in range(rows):
            del(self._data[position])
        self.endRemoveRows()
        self.layoutChanged.emit()
        return True

class MainWindow(QtWidgets.QMainWindow):

    def __init__(self):
        super().__init__()
        
        self.insertaction = QtWidgets.QAction("Insert")
        self.insertaction.triggered.connect(self.insert_row)
        self.deletedaction = QtWidgets.QAction("Delete")
        self.deletedaction.triggered.connect(self.delete_row)
        
        toolbar = QtWidgets.QToolBar("Edit")
        toolbar.addAction(self.insertaction)
        toolbar.addAction(self.deletedaction)
        self.addToolBar(toolbar)
        

        self.table = QtWidgets.QTableView()

        data = [
          [1, 9, 2],
          [1, 0, -1],
          [3, 5, 2],
          [3, 3, 2],
          [5, 8, 9],
        ]

        self.model = TableModel(data)
        self.table.setModel(self.model)

        self.setCentralWidget(self.table)
        
    def insert_row(self):
        index = self.table.currentIndex()
        print(index)
        self.model.insertRows(index.row(), 1, index, None)
    
    def delete_row(self):
        index = self.table.currentIndex()
        self.model.removeRows(index.row(), 1, index)
        


app=QtWidgets.QApplication(sys.argv)
window=MainWindow()
window.show()
app.exec_()
1 Like

Thank you Martin, this worked for me. Great help.

For others that like me have a proxy model in between the Model and the QTableView I found out that I had to add
self.layoutAboutToBeChanged.emit()
in the begining of the def insertRows() or def removeRows().
Without that I found out that I could only delete one row or insert one row before the application would be non-responsive, or the delete or insert didnt work at alla after one row insert or delete.

so final code for QSortFilterProxyModel
In the sub-classed QAbstractTableModel

def insertRows(self, position, rows, QModelIndex, parent):
    self.layoutAboutToBeChanged.emit()
    self.beginInsertRows(QModelIndex, position, position+rows-1)
    default_row = ['']*len(self._data[0])
    for i in range(rows):
        self._data.insert(position, default_row)
    self.endInsertRows()
    self.layoutChanged.emit()
    return True

def removeRows(self, position, rows, QModelIndex):
    self.layoutAboutToBeChanged.emit()
    self.beginRemoveRows(QModelIndex, position, position+rows-1)
    for i in range(rows):
        del(self._data[position])
    self.endRemoveRows()
    self.layoutChanged.emit()
    return True

In the Mainwindow the delete_row() would be like this

def delete_row(self):
    proxy_index = self.table.currentIndex()
    index = self.proxyModel.mapToSource(proxy_index)
    self.model.removeRows(index.row(), 1, index)
2 Likes

@David_Hansson Nice thanks for the tip. I’m probably going to need that snippet soon. Saved.