【问题标题】:How can I properly implement QSortFilterProxyModel.parent() to handle a virtual column?如何正确实现 QSortFilterProxyModel.parent() 来处理虚拟列?
【发布时间】:2021-09-01 09:11:58
【问题描述】:

我有以下工作代码,它会打开一个 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)
(...)

我很确定我错过了什么,但我不知道它是什么......

【问题讨论】:

  • 嗯,刚刚意识到我不断收到Can't select indexes from different model or with different parents 消息,它不允许我选择任何文件,所以我肯定做错了什么????

标签: python qt pyside2 model-view


【解决方案1】:

主要问题是父级不应无效,即使对于“虚拟”索引也是如此。

另外,为了与fake column正确交互,必须考虑以下三个方面:

  • createIndex() 需要父级的internalId(),否则即使父级不同,同一对行/列也会有相同的索引;
  • flags() 必须返回有效标志,这种情况下可以返回第一行同级的标志;
  • sibling() 必须为虚拟列返回 self.index() 的结果,或者使用 有效 起始索引来计算兄弟;
  • mapToSource 应该返回一个有效的 source 索引,以便视图可以正确访问其数据;无效索引通常被认为是模型的 root,返回它代表一个问题:如果双击索引,文件对话框会尝试打开它,并且由于无效索引被认为是根文件系统模型(这是一个“文件夹”),然后导航到它;
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)

【讨论】:

  • 非常感谢!现在唯一不能按预期工作的是在项目的虚拟列上双击(或单击 + Enter),因为它总是转到 (我的)计算机,,但我很感激这不是' t原始问题,我仍然需要探索如何处理输入项目。
  • 虽然 createIndex() 需要一个唯一 ID 这个事实很有意义,但我不明白为什么将它传递给父级的 internalId() 不会导致冲突,作为一个索引ID 已存在。这是如何工作的?
  • @Unai 查看选择问题的更新(我忘记了选择问题)。关于您的另一个问题, internalId 不是每个索引的唯一 id,但它更多的是抽象坐标系中的参考。您可以将其视为 3d 系统的 z 值,其中行是 x,列是 y:对于不同的 xy 值,您可以拥有相同的 z,这并不意味着它们代表同一点。如果想要更准确的版本,可以使用兄弟的内部 id:index = self.createIndex(row, column, self.index(row, 0, parent).internalId())
  • 注意:我不知道在内部如何确切地使用索引ID/指针。经过一些思考和测试,我相信,确实,使用现有兄弟索引的 id 可能 更合适,即使上面的代码运行良好;这是因为,即使 id 引用不“正确”,它仍然可以确保 row/column/id 的组合对于第四列的每个索引都是唯一的。
  • 令人着迷,再次感谢!坐标类比非常有意义,但我仍然不太明白为什么我们可以安全地假设,对于任何对self.index(row, column, parent)row != parent.row() or column != parent.column() 的调用。兄弟方法对我来说似乎更合乎逻辑,但我可能会遗漏一些东西。
猜你喜欢
  • 2012-04-19
  • 2013-02-25
  • 1970-01-01
  • 2018-06-20
  • 1970-01-01
  • 2012-07-04
  • 1970-01-01
  • 1970-01-01
  • 2011-10-08
相关资源
最近更新 更多