Eurydice
Eurydice

Reputation: 8449

Removing rows of a QSortFilterProxyModel behaves badly

I have an application which contains a QTableView for which I would like to have the possibility to sort its contents but also to remove one or more rows. Below is an example code that implements this.

from PyQt5 import QtCore, QtWidgets

class NeXuSFilesModel(QtCore.QAbstractTableModel):

    fields = ['col1','col2']

    def __init__(self,parent):
        super(NeXuSFilesModel,self).__init__(parent)

        self._nexus_contents = [['aaa','111'],['bbb','222'],['ccc','333']]

    def columnCount(self, parent=None):

        return 2

    def data(self,index,role):

        if not index.isValid():
            return QtCore.QVariant()

        row = index.row()
        col = index.column()

        if role == QtCore.Qt.DisplayRole:
            return str(self._nexus_contents[row][col])

        else:
            return QtCore.QVariant()

    def headerData(self, index, orientation, role):

        if role == QtCore.Qt.DisplayRole:
            if orientation == QtCore.Qt.Horizontal:
                return NeXuSFilesModel.fields[index]
            else:
                return index + 1
        return None

    def removeRow(self, row, parent):

        self.beginRemoveRows(QtCore.QModelIndex(),row,row+1)
        del self._nexus_contents[row]
        self.endRemoveRows()

        return True

    def rowCount(self, parent=None):

        return len(self._nexus_contents)
 

class NeXuSDataTableView(QtWidgets.QTableView):

    def __init__(self,parent):
        super(NeXuSDataTableView,self).__init__(parent)

        self.horizontalHeader().setStretchLastSection(True)

    def keyPressEvent(self, event):
        if event.key() == QtCore.Qt.Key_Delete:
            model = self.model()
            selected_indexes = self.selectionModel().selectedRows()
            source_indexes_rows = sorted([model.mapToSource(index).row() for index in selected_indexes],reverse=True)
            for row in source_indexes_rows:
                model.sourceModel().removeRow(row,QtCore.QModelIndex())

        super(NeXuSDataTableView, self).keyPressEvent(event)

class MainWindow(QtWidgets.QWidget):

    def __init__(self):

        super(MainWindow,self).__init__()

        self._table = NeXuSDataTableView(self)
        model = NeXuSFilesModel(self)

        proxy_model = QtCore.QSortFilterProxyModel()
        proxy_model.setSourceModel(model)

        self._table.setModel(proxy_model)
        self._table.setSortingEnabled(True)

        mainLayout = QtWidgets.QVBoxLayout()
        mainLayout.addWidget(self._table)

        self.setLayout(mainLayout)
        self.show()

if __name__ == '__main__':
    app = QtWidgets.QApplication([])
    _ = MainWindow()
    app.exec_()

When I run that code, I fall into several problems for which I could not find the solution or could not understand the explanations given by the various sources I could find.

  1. When the program starts, the data is showed initially in the wrong order. Indeed, it is displayed in descending order whereas I would like to display it in ascending order
  2. When I remove one item, it removes actually two items !

Would you have any idea about what is wrong with my implementation ?

Upvotes: 0

Views: 193

Answers (1)

Flow
Flow

Reputation: 731

About both your questions:

  1. proxy_model.sort(0, QtCore.Qt.AscendingOrder) after self._table.sortSortingEnabled(True) results in an ascending order:
        self._table.setSortingEnabled(True)
        proxy_model.sort(0, QtCore.Qt.AscendingOrder)
        mainLayout = QtWidgets.QVBoxLayout()
        mainLayout.addWidget(self._table)

        self.setLayout(mainLayout)
        self.show()
  1. Using self.beginRemoveRows(QtCore.QModelIndex(),row,row) will remove only one row.

Upvotes: 1

Related Questions