Search code examples
pythonqtpyqttableviewqstandarditemmodel

How to inverse QTableView selection


The code below creates a single QTableView and QPushButton. When the button is clicked I would like to toggle the current selection (inverse it): what used to be selected is now deselected and what used to be deselected is selected. Finally I would like to remove (delete) the rows that are now selected leaving only those that are deselected.

Question: How to achieve it?

enter image description here

from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
app = QApplication([])


class Dialog(QDialog):
    def __init__(self, parent=None):
        super(Dialog, self).__init__(parent)
        self.setLayout(QVBoxLayout())
        self.view = QTableView(self)
        self.view.setSelectionBehavior(QTableWidget.SelectRows)
        self.view.setSortingEnabled(True)
        self.view.sortByColumn(0, Qt.DescendingOrder)

        self.view.setModel(QStandardItemModel(4, 4))
        for each in [(row, col, QStandardItem('item %s_%s' % (row, col))) for row in range(4) for col in range(4)]:
            self.view.model().setItem(*each)

        self.layout().addWidget(self.view)

        btn1 = QPushButton('Invert selection then remove what selected')
        btn1.clicked.connect(self.invertSelectionRemoveSelected)
        self.layout().addWidget(btn1)
        self.resize(500, 250)
        self.show()

    def invertSelectionRemoveSelected(self):
        print 'invertSelectionRemoveSelected'


dialog = Dialog()
app.exec_()

Solution

  • You have to iterate to get the QModelIndex associated with each cell, and use the QItemSelection to invert the selection of each cell.

    def invertSelectionRemoveSelected(self):
        model = self.view.model()
        for i in range(model.rowCount()):
            for j in range(model.columnCount()):
                ix = model.index(i, j)
                self.view.selectionModel().select(ix, QItemSelectionModel.Toggle)
        # delete rows
        for ix in reversed(self.view.selectionModel().selectedRows()):
            model.removeRow(ix.row())
    

    Another Solution:

    From your request I understand that you want to eliminate the unselected rows, and deselect all the others afterwards. So the next solution does it directly.

    def invertSelectionRemoveSelected(self):
        model = self.view.model()
        rows_selected =[ix.row() for ix in self.view.selectionModel().selectedRows()]
        [model.removeRow(i) for i in reversed(range(model.rowCount())) if i not in rows_selected]
        self.view.clearSelection()