如何正确实施 QSortFilterProxyModel.parent() 来处理虚拟列?
How can I properly implement QSortFilterProxyModel.parent() to handle a virtual column?
我有以下工作代码,它打开了一个 QFileDialog
,其中有一个额外的列再次显示文件名(我知道这毫无意义,但这是简化我的问题的结果):
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_()
如您所见,parent()
返回第 4 列项目的无效索引,而我正在检索 data()
中的实际父项,这并不理想。但是,如果我尝试以下操作,它会因访问冲突而退出:
(...)
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)
(...)
我也尝试利用 QModelIndex
的内部指针,结果相同(访问冲突):
# 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)
(...)
很确定我漏掉了什么,但我不知道是什么……
主要问题是 parent 不应无效,即使对于“虚拟”索引也是如此。
另外,为了与假专栏正确互动,必须考虑以下三个方面:
- parent 的
internalId()
对 createIndex()
是必需的,否则即使它们有不同的 row/column 对,你也会有相同的索引parents;
flags()
必须return有效的flags,这种情况下可以return第一行兄弟的flag;
sibling()
必须是 return 虚拟列的 self.index()
的结果,或者使用 valid 起始索引来计算兄弟;
mapToSource
应该 return 一个有效的 source 索引,以便视图可以正确访问其数据;无效的索引通常被认为是模型的 root,returning 它表示一个问题:如果双击索引,文件对话框会尝试打开它,并且由于无效索引被认为是文件系统模型的根(这是一个“文件夹”),因此它将导航到它;
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)
我有以下工作代码,它打开了一个 QFileDialog
,其中有一个额外的列再次显示文件名(我知道这毫无意义,但这是简化我的问题的结果):
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_()
如您所见,parent()
返回第 4 列项目的无效索引,而我正在检索 data()
中的实际父项,这并不理想。但是,如果我尝试以下操作,它会因访问冲突而退出:
(...)
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)
(...)
我也尝试利用 QModelIndex
的内部指针,结果相同(访问冲突):
# 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)
(...)
很确定我漏掉了什么,但我不知道是什么……
主要问题是 parent 不应无效,即使对于“虚拟”索引也是如此。
另外,为了与假专栏正确互动,必须考虑以下三个方面:
- parent 的
internalId()
对createIndex()
是必需的,否则即使它们有不同的 row/column 对,你也会有相同的索引parents; flags()
必须return有效的flags,这种情况下可以return第一行兄弟的flag;sibling()
必须是 return 虚拟列的self.index()
的结果,或者使用 valid 起始索引来计算兄弟;mapToSource
应该 return 一个有效的 source 索引,以便视图可以正确访问其数据;无效的索引通常被认为是模型的 root,returning 它表示一个问题:如果双击索引,文件对话框会尝试打开它,并且由于无效索引被认为是文件系统模型的根(这是一个“文件夹”),因此它将导航到它;
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)