how to preserve selection when sorting QTableView

Viewed 438

How can I preserve the selection of items when I sort the table? In the below example the selection is always fixed to the row index i.e. if I select first row, then after sorting always first row is selected, not the actual row that I had selected.

import sys

from PySide2 import QtWidgets, QtGui, QtCore
from PySide2.QtCore import Qt


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

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

    def sort(self, column, order):
        if order == Qt.DescendingOrder:
            rev = True
        else:
            rev = False
        self.layoutAboutToBeChanged.emit()
        self._data.sort(key=lambda x: x[column], reverse=rev)
        self.layoutChanged.emit()

    def rowCount(self, parent=None):
        return len(self._data)

    def columnCount(self, parent=None):
        return len(self._data[0])


class Main(QtWidgets.QDialog):
    def __init__(self, data):
        super().__init__()
        self.layout = QtWidgets.QVBoxLayout()
        self.table = QtWidgets.QTableView()
        self.table.setSortingEnabled(True)
        self.table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectionBehavior.SelectRows)
        self.model = TableModel(data)
        self.table.setModel(self.model)
        self.layout.addWidget(self.table)
        self.setLayout(self.layout)


def main():
    app = QtWidgets.QApplication(sys.argv)
    data = [
        [1,2,3,4],
        [5,6,7,8],
        [6,5,4,3],
        [2,1,0,9]
    ]
    m = Main(data)
    m.show()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()
1 Answers

When implementing the sort method you are modifying the data and there is no way that the view knows the new position of the items, instead of modifying the data you must modify the indices using a QSortFilterProxyModel:

import sys

from PySide2 import QtCore, QtGui, QtWidgets


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

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

    def rowCount(self, parent=None):
        return len(self._data)

    def columnCount(self, parent=None):
        return len(self._data[0])


class Main(QtWidgets.QDialog):
    def __init__(self, data):
        super().__init__()
        self.layout = QtWidgets.QVBoxLayout()
        self.table = QtWidgets.QTableView()
        self.table.setSortingEnabled(True)
        self.table.setSelectionBehavior(
            QtWidgets.QAbstractItemView.SelectionBehavior.SelectRows
        )
        self.model = TableModel(data)
        self.proxy = QtCore.QSortFilterProxyModel()
        self.proxy.setSourceModel(self.model)
        self.table.setModel(self.proxy)
        self.layout.addWidget(self.table)
        self.setLayout(self.layout)


def main():
    app = QtWidgets.QApplication(sys.argv)
    data = [[1, 2, 3, 4], [5, 6, 7, 8], [6, 5, 4, 3], [2, 1, 0, 9]]
    m = Main(data)
    m.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()
Related