I have the following working code, which opens a QFileDialog
with an extra column that shows the file name again (pointless, I know, but it’s a result of simplifying my issue):
from PySide2 import QtCore, QtWidgets
class MyProxyModel(QtCore.QSortFilterProxyModel):
def __init__(self, parent=None):
super(MyProxyModel, self).__init__(parent)
self._parents = {}
def mapToSource(self, index):
if index.column() == 4:
return QtCore.QModelIndex()
return super(MyProxyModel, self).mapToSource(index)
def columnCount(self, index):
return 5
def data(self, index, role=QtCore.Qt.DisplayRole):
if role == QtCore.Qt.DisplayRole and index.column() == 4:
return self.index(index.row(), 0, self._parents[index]).data(role)
return super(MyProxyModel, self).data(index, role)
def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
if section == 4 and orientation == QtCore.Qt.Horizontal \
and role == QtCore.Qt.DisplayRole:
return 'My Column'
return super(MyProxyModel, self).headerData(section, orientation, role)
def index(self, row, column, parent=QtCore.QModelIndex()):
if column == 4:
index = self.createIndex(row, column)
self._parents[index] = parent
return index
return super(MyProxyModel, self).index(row, column, parent)
def parent(self, index):
if index.column() == 4:
return QtCore.QModelIndex()
return super(MyProxyModel, self).parent(index)
QtWidgets.QApplication([])
dialog = QtWidgets.QFileDialog()
dialog.setOption(dialog.DontUseNativeDialog, True)
dialog.setProxyModel(MyProxyModel(dialog))
dialog.exec_()
As you can see, parent()
is returning an invalid index for items of column 4, and instead I’m retrieving the actual parent inside data()
, which isn’t ideal. But if I try the following, it exits with an access violation:
(...)
def data(self, index, role=QtCore.Qt.DisplayRole):
if role == QtCore.Qt.DisplayRole and index.column() == 4:
# Either return causes access violation.
return self.index(index.row(), 0, self.parent(index)).data(role)
return self.index(index.row(), 0, index.parent()).data(role)
return index.sibling(index.row(), 0).data(role)
return super(MyProxyModel, self).data(index, role)
(...)
def parent(self, index):
if index.column() == 4:
return self._parents[index]
return super(MyProxyModel, self).parent(index)
(...)
I also tried leveraging QModelIndex
’s internal pointer, with the same result (access violation):
# No __init__() defined; data() exactly like above.
(...)
def index(self, row, column, parent=QtCore.QModelIndex()):
if column == 4:
return self.createIndex(row, column, parent)
return super(MyProxyModel, self).index(row, column, parent)
def parent(self, index):
if index.column() == 4:
return index.internalPointer()
return super(MyProxyModel, self).parent(index)
(...)
Pretty sure I’m missing something, but I can’t figure out what it is…
The main problem is that the parent should not be invalid, even for a "virtual" index.
Also, in order to properly interact with the fake column, the following three aspects must be considered:
internalId()
of the parent is required for createIndex()
, otherwise you'll have the same index for the same pair of row/column even if they have different parents;flags()
must return valid flags, in this case you can return the flag of the sibling in the first row;sibling()
must be return the result of self.index()
for the virtual column, or use a valid starting index to compute the sibling;mapToSource
should return a valid source index so that the view can properly access its data; an invalid index is usually considered the root of the model, and returning it represents an issue: if you double click an index, the file dialog tries to open it, and since an invalid index is considered the root of the file system model (which is a "folder"), it will then navigate to it;class MyProxyModel(QtCore.QSortFilterProxyModel):
# ...
def mapToSource(self, index):
if index.column() == 4:
index = index.sibling(index.row(), 0)
return super(MyProxyModel, self).mapToSource(index)
def index(self, row, column, parent=QtCore.QModelIndex()):
if column == 4:
index = self.createIndex(row, column, parent.internalId())
self._parents[index] = parent
return index
return super(MyProxyModel, self).index(row, column, parent)
def parent(self, index):
if index.column() == 4:
return self._parents[index]
return super(MyProxyModel, self).parent(index)
def flags(self, index):
if index.column() == 4:
return self.flags(index.sibling(index.row(), 0))
return super().flags(index)
def sibling(self, row, column, idx):
if column == 4:
return self.index(row, column, idx.parent())
elif idx.column() == 4:
idx = self.index(idx.row(), 0, idx.parent())
return super().sibling(row, column, idx)