Uploaded image for project: 'Qt for Python'
  1. Qt for Python
  2. PYSIDE-611

selectedIndexes returns empty list when editing sorted column cells

    XMLWordPrintable

    Details

    • Type: Bug
    • Status: Reported
    • Priority: P3: Somewhat important
    • Resolution: Unresolved
    • Affects Version/s: 5.6
    • Fix Version/s: None
    • Component/s: PySide
    • Labels:

      Description

      I want to use a QTableView with a QAbstractTableModel/QSortFilterProxyModel to offer the possibility to select multiple table cells, make an edit and have this edit applied to the model representing all the selected table cells. In order to achieve this, the model's selectedIndexes is queried on the proxy model's dataChanged.

      It seems that PySide2 has a bug because self.view.selectionModel().selectedIndexes() in the code below does not return the selected elements but an empty list when you edit the cells of the sorted QTableView column, resulting in not all selected cells updating as a result of an edit.

      See here: https://imgur.com/a/KcHFU

      For the other columns which are not the sorted one, the multi-select followed by edit updates the model and view properly

      class MyTableModel(QtCore.QAbstractTableModel):
          def __init__(self, table_data, parent=None):
              QtCore.QAbstractTableModel.__init__(self, parent)
              self.table_data = table_data
      
          def rowCount(self, parent):
              return len(self.table_data)
      
          def columnCount(self, parent):
              return len(self.table_data[0])
      
          def flags(self, index):
              original_flags = super(MyTableModel, self).flags(index)
              return original_flags | QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
      
          def data(self, index, role=QtCore.Qt.DisplayRole):
              if role == QtCore.Qt.DisplayRole:
                  row = index.row()
                  column = index.column()
                  item = index.internalPointer()
                  if item is not None:
                      print(item)
                  value = self.table_data[row][column]
                  return value
      
          def setData(self, index, value, role=QtCore.Qt.EditRole):
              if role == QtCore.Qt.EditRole:
                  row = index.row()
                  column = index.column()
                  self.table_data[row][column] = value
                  self.dataChanged.emit(index, index)
                  return True
              return QtCore.QAbstractTableModel.setData(self, index, value, role)
      
      
      class Widget(QtWidgets.QWidget):
          def __init__(self, *args, **kwargs):
              QtWidgets.QWidget.__init__(self, *args, **kwargs)
              self.view = QtWidgets.QTableView()
              self.setLayout(QtWidgets.QVBoxLayout())
              self.layout().addWidget(self.view)
      
              table_data = [['one', 'two', 'three'], ['four', 'five', 'six']]
              proxy_model = QtCore.QSortFilterProxyModel()
              model = MyTableModel(table_data=table_data)
              proxy_model.setSourceModel(model)
              proxy_model.setDynamicSortFilter(True)
              self.view.setModel(proxy_model)
              proxy_model.dataChanged.connect(self.on_data_changed)
      
              self.view.setSortingEnabled(True)  # requires proxy model
              self.view.sortByColumn(0, QtCore.Qt.AscendingOrder)
              self.view.horizontalHeader().setStretchLastSection(True)
              self.view.horizontalHeader().setSectionsMovable(True)
      
          def on_data_changed(self, _from, _to):
              model = _from.model() # proxy model
              model.blockSignals(True)
              for index in self.view.selectionModel().selectedIndexes():
                  model.setData(index, _from.data())
              model.blockSignals(False)
      
      if __name__ == '__main__':
          app = QtWidgets.QApplication(sys.argv)
          w = Widget()
          w.show()
          sys.exit(app.exec_())
      

      Workaround:

      class Widget(QtWidgets.QWidget):
          def __init__(self, *args, **kwargs):
              [...]
              self.view.horizontalHeader().setSectionsMovable(True)
      
              self.view.selectionModel().selectionChanged.connect(self.on_selectionChanged)
              self.currentSelected = []
      
          def on_selectionChanged(self, selected, deselected):
              for ix in selected.indexes():
                 if ix not in self.currentSelected:
                  self.currentSelected.append(ix)
              for ix in deselected.indexes():
                  if ix in self.currentSelected:
                      self.currentSelected.remove(ix)
      
          def on_data_changed(self, _from, _to):
              model = _from.model() # proxy model
              model.blockSignals(True)
              pindexes = [QtCore.QPersistentModelIndex(ix) for ix in self.currentSelected]
              for index in pindexes:
                  model.setData(index, _from.data())
              model.blockSignals(False)
      

        Attachments

        1. 6LHzrG4.gif
          6LHzrG4.gif
          19 kB
        2. selectionIndexIssue.mov
          602 kB
        No reviews matched the request. Check your Options in the drop-down menu of this sections header.

          Activity

            People

            • Assignee:
              crmaurei Cristian Maureira-Fredes
              Reporter:
              fredrikaverpil Fredrik Averpil
            • Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

              • Created:
                Updated:

                Gerrit Reviews

                There are no open Gerrit changes