Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-133227

QTableView.selectionChanged emitted twice on row deletion

    XMLWordPrintable

Details

    • Bug
    • Resolution: Unresolved
    • P2: Important
    • None
    • 6.8.1
    • Core: Item Models
    • None
    • Windows

    Description

      When a simple QTableView is used to display the data of a QAbstractTableModel and a row is selected and removed, the following QTableView.selectionChanged signal is emitted twice and second time the `selected: QItemSelection` arguments provides empty selection, even if actually the next row is correctly selected.

      With the mwe at the bottom, it's easy to reproduce this behavior. Simply select a row, e.g. row with index 2, then delete it using the Delete key.

      You'll see an output similar like below, note the second "selectionChanged ...", which shouldn't happen.

      CustomTableModel.removeRow(row=2)
      selectionChanged: indexes=[<PySide6.QtCore.QModelIndex(3,0,0x0,...>] actual_indexes=[<PySide6.QtCore.QModelIndex(3,0,0x0,...>]
      selectionChanged: indexes=[] actual_indexes=[<PySide6.QtCore.QModelIndex(3,0,0x0,...>]
      

      MWE

      import PySide6
      from PySide6 import QtGui, QtCore, QtWidgets
      
      print(PySide6.__version__)  # Reproducible with 6.8.1
      
      class CustomTableModel(QtCore.QAbstractTableModel):
          def __init__(self):
              super().__init__()
              self._data = [i for i in range(0,10)]
      
          def rowCount(self, parent=QtCore.QModelIndex()):
              return len(self._data)
      
          def columnCount(self, parent=QtCore.QModelIndex()):
              return 1
      
          def data(self, index, role=QtCore.Qt.DisplayRole):
              column = index.column()
              row = index.row()
      
              if role == QtCore.Qt.DisplayRole:
                  if column == 0 and row <= self.rowCount():
                      return str(self._data[row])
      
              return None
      
          def removeRow(self, row: int, parent: QtCore.QModelIndex | None = None) -> bool:
              print(f"CustomTableModel.removeRow({row=})")
      
              if self.rowCount() <= 0:
                  return False
      
              self.beginRemoveRows(QtCore.QModelIndex(), row, row)
              self._data.pop(row)
              self.endRemoveRows()
      
              return True
      
      
      class CustomTableView(QtWidgets.QTableView):
      
          def keyPressEvent(self, event: QtGui.QKeyEvent) -> None:
              if event.matches(QtGui.QKeySequence.StandardKey.Delete):
                  selected_row = self.selectionModel().selectedRows()[0].row() if self.selectionModel().selectedRows() else -1
                  self.model().removeRow(selected_row)
              super().keyPressEvent(event)
      
          def selectionChanged(self, selected: QtCore.QItemSelection, deselected: QtCore.QItemSelection) -> None:
              super().selectionChanged(selected, deselected)
              indexes = selected.indexes()
              actual_indexes = self.selectionModel().selection().indexes()
              print(f"selectionChanged: {indexes=} {actual_indexes=}")
      
      
      
      class Widget(QtWidgets.QWidget):
          def __init__(self):
              super().__init__()
      
              # Getting the Model
              self.model = CustomTableModel()
      
              self.table_view = CustomTableView()
              self.table_view.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectionBehavior.SelectRows)
              self.table_view.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.SingleSelection)
              self.table_view.setModel(self.model)
      
              self.main_layout = QtWidgets.QHBoxLayout()
              self.main_layout.addWidget(self.table_view)
              self.setLayout(self.main_layout)
      
      if __name__ == "__main__":
          app = QtWidgets.QApplication()
          widget = Widget()
          widget.show()
          app.exec()
      

      Attachments

        No reviews matched the request. Check your Options in the drop-down menu of this sections header.

        Activity

          People

            dfaure_kdab David Faure
            bbc131 Björn Bissinger
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

              Created:
              Updated:

              Gerrit Reviews

                There are no open Gerrit changes