Tableview delete rows works only one time

Heads up! You've already completed this tutorial.

Aashish_Gogna | 2022-08-09 11:10:54 UTC | #1

Hello Team,

I have created Qtreeview and Qtableview and set their models to respective Abstract classes. Functionality is: doubleclclicking on an element present in the treeview will create a new row in the tableview with some additional columns. One function in My code is to delete these entries when a button is clicked, which successfully deletes the entire table only once per run, and gives no result for further clicks on delete button even if the entries are present.

delete rows code is:

python
model.removeRows(position=0, rows=model.rowCount(0))

where removeRows function in tablemodel is written as:

python
    def removeRows(self, position, rows=1, index=QtCore.QModelIndex()):
        print(
            "\n\t\t ...removeRows() Starting position: '%s'" % position, 'with the total rows to be deleted: ', rows)
        self.beginRemoveRows(QtCore.QModelIndex(), position, position + rows - 1)
        self.endRemoveRows()

Please suggest why the deletion activity deletes the rows only once per run and why the rowcount is always an increasing number even though the rows have already been deleted?

the code is :slight_smile:

Create GUI Applications with Python & Qt5 by Martin Fitzpatrick — (PyQt5 Edition) The hands-on guide to making apps with Python — Over 10,000 copies sold!

More info Get the book

python
class TableModel(QtCore.QAbstractTableModel):
    def __init__(self, data, data_header):
        super().__init__()
        self._data = data
        self.header = data_header

    def data(self, index, role):
        if role == Qt.DisplayRole:
            # See below for the nested-list data structure.
            # .row() indexes into the outer list,
            # .column() indexes into the sub-list
            return self._data[index.column()][index.row()]
        elif role == Qt.EditRole:
            return self._data[index.column()][index.row()]
        elif role == Qt.BackgroundRole:
            # See below for the data structure.
            value = self._data[index.column()][index.row()]
            if index.column() == 6:
                if value == "Not Started":
                    return QtGui.QColor("yellow")
                elif value == "Started":
                    return QtGui.QColor("orange")
                elif value == "Passed":
                    return QtGui.QColor("green")
                elif value == "Failed":
                    return QtGui.QColor("red")

        elif role == Qt.TextAlignmentRole:
            value = self._data[index.column()][index.row()]
            if isinstance(value, int) or isinstance(value, float):
                # Align right, vertical middle.
                return Qt.AlignVCenter + Qt.AlignRight

        elif role == Qt.ForegroundRole:
            value = self._data[index.column()][index.row()]
            if (
                    (isinstance(value, int) or isinstance(value, float))
                    and value < 0
            ):
                return QtGui.QColor('red')
        else:
            return QtCore.QVariant()

    def flags(self, index):

        if index.column() == 0 or index.column() == 1 or index.column() == 6 or index.column() == 7:
            return Qt.ItemIsSelectable | Qt.ItemIsEnabled
        else:
            return Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsEditable

    def update(self, dataIn, data_header):
        print('Updating Model')
        self._data = dataIn
        self.header = data_header
        print('Datatable : {0}'.format(self._data))

    def rowCount(self, index):
        # The length of the outer list.
        return len(self._data[0])

    def columnCount(self, index):
        # The following takes the first sub-list, and returns
        # the length (only works if all rows are an equal length)
        return len(self._data)

    def headerData(self, col, orientation, role):
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            return self.header[col]
        return None

    def setData(self, index, value, role=QtCore.Qt.EditRole):
        if index.isValid() and 0 <= index.row():
            if role == QtCore.Qt.EditRole:
                self._data[index.column()][index.row()] = value
                print("edit", value)
                self.dataChanged.emit(index, index)  # NOT WORKING
                return True
            else:
                return False
        else:
            return False

    @QtCore.pyqtSlot()
    def addrow(self):
        # print(self.rowCount(0))
        self.beginInsertRows(QtCore.QModelIndex(), 0, 0)
        ret = (self.insertRows(self.rowCount(0), 1))
        self.endInsertRows()
        # print(ret)

    def removeRows(self, position, rows=1, index=QtCore.QModelIndex()):
        print(
            "\n\t\t ...removeRows() Starting position: '%s'" % position, 'with the total rows to be deleted: ', rows)
        self.beginRemoveRows(QtCore.QModelIndex(), position, position + rows - 1)
        # del self._data[position]
        # self.items = self.items[:position] + self.items[position + rows:]
        self.endRemoveRows()

    def delete_row(self, index):
        self.removeRows(index.row(), 1, QtCore.QModelIndex())

martin | 2021-07-23 14:47:09 UTC | #2

Hey! I'm a bit confused by your example code as there is nothing there to actually remove the data from the model. In the second (larger block) there are lines to do this, but they are commented out.

The beginRemoveRows and endRemoveRows is just there to notify the view that the data is being modified, you still need to make the modifications to the data yourself.

For a list of rows, you can just delete the start index n times (since subsequent elements will shift up). For example...

python
self.beginRemoveRows(QtCore.QModelIndex(), position, position + rows - 1)
for _ in range(rows):
    del self._data[position]
self.endRemoveRows()

The complete guide to packaging Python GUI applications with PyInstaller.
[[ discount.discount_pc ]]% OFF for the next [[ discount.duration ]] [[discount.description ]] with the code [[ discount.coupon_code ]]

Purchasing Power Parity

Developers in [[ country ]] get [[ discount.discount_pc ]]% OFF on all books & courses with code [[ discount.coupon_code ]]
Well done, you've finished this tutorial! Mark As Complete
[[ user.completed.length ]] completed [[ user.streak+1 ]] day streak

Tableview delete rows works only one time was written by Martin Fitzpatrick .

Martin Fitzpatrick has been developing Python/Qt apps for 8 years. Building desktop applications to make data-analysis tools more user-friendly, Python was the obvious choice. Starting with Tk, later moving to wxWidgets and finally adopting PyQt.