【问题标题】:(Py)Qt: QSortFilterProxyModel calls index() with parent from wrong model(Py)Qt: QSortFilterProxyModel 使用来自错误模型的父级调用 index()
【发布时间】:2014-04-11 04:20:46
【问题描述】:

QSortFilterProxyModel 有一个奇怪的问题。我在我的 QTreeView 中执行此操作:

class CompletionView(QTreeView):

    def __init__(self, parent=None):
        super().__init__(parent)
        self.setModel(QSortFilterProxyModel())

        m1 = CompletionModel()
        print("Model 1: {}".format(m1))
        m1.init_data({'test1': [('one', 'two'), ('three', 'four')]})
        self.model().setSourceModel(m1)
        self.expandAll()

        m2 = CompletionModel()
        print("Model 2: {}".format(m2))
        m2.init_data({'test': [('five', 'six'), ('seven', 'eight')]})
        self.model().setSourceModel(m2)
        self.expandAll()

这是我的 CompletionModel,我不能再简化了:

class CompletionModel(QAbstractItemModel):
    def __init__(self, parent=None):
        super().__init__(parent)
        self._id_map = {}
        self._root = CompletionItem([""] * 2)
        self._id_map[id(self._root)] = self._root

    def _node(self, index):
        if index.isValid():
            return self._id_map[index.internalId()]
        else:
            return self._root

    def init_data(self, data):
        for (cat, items) in data.items():
            newcat = CompletionItem([cat], self._root)
            self._id_map[id(newcat)] = newcat
            self._root.children.append(newcat)
            for item in items:
                newitem = CompletionItem(item, newcat)
                self._id_map[id(newitem)] = newitem
                newcat.children.append(newitem)

    def columnCount(self, parent=QModelIndex()):
        return self._root.column_count()

    def rowCount(self, parent=QModelIndex()):
        if parent.column() > 0:
            return 0

        if not parent.isValid():
            pitem = self._root
        else:
            pitem = self._id_map[parent.internalId()]
        return len(pitem.children)

    def data(self, index, role=Qt.DisplayRole):
        if not index.isValid():
            return QVariant()
        try:
            item = self._id_map[index.internalId()]
        except KeyError:
            return QVariant()
        try:
            return QVariant(item.data(index.column(), role))
        except (IndexError, ValueError):
            return QVariant()

    def headerData(self, section, orientation, role=Qt.DisplayRole):
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            return QVariant(self._root.data(section))
        return QVariant()

    def index(self, row, column, parent=QModelIndex()):
        if parent.model() is not None and parent.model() is not self:
            raise ValueError("Model mismatch: parentmodel {}, self {}".format(parent.model(), self))
        if (0 <= row < self.rowCount(parent) and
                0 <= column < self.columnCount(parent)):
            pass
        else:
            return QModelIndex()

        if not parent.isValid():
            parent_item = self._root
        else:
            parent_item = self._id_map[parent.internalId()]

        child_item = parent_item.children[row]
        if child_item:
            index = self.createIndex(row, column, id(child_item))
            self._id_map.setdefault(index.internalId(), child_item)
            return index
        else:
            return QModelIndex()

    def parent(self, index):
        if not index.isValid():
            return QModelIndex()
        item = self._id_map[index.internalId()].parent
        if item == self._root or item is None:
            return QModelIndex()
        return self.createIndex(item.row(), 0, id(item))


class CompletionItem():
    def __init__(self, data, parent=None):
        self.parent = parent
        self.children = []
        self._data = data

    def data(self, column, role=Qt.DisplayRole):
        if role == Qt.DisplayRole:
            return self._data[column]
        else:
            raise ValueError("Invalid role {}".format(role))

    def column_count(self):
        return len(self._data)

    def row(self):
        if self.parent:
            return self.parent.children.index(self)
        return 0

请注意,如果我遇到错误的父母,我会在 index() 中提出 ValueError

现在当我执行我的示例脚本(粘贴in my pastebin)时,会发生这种情况:

Model 1: <__main__.CompletionModel object at 0x7fefe1ccd770>
Model 2: <__main__.CompletionModel object at 0x7fefe1ccd808>
Traceback (most recent call last):
  File "model.py", line 59, in index
    raise ValueError("Model mismatch: parentmodel {}, self {}".format(parent.model(), self))
ValueError: Model mismatch: parentmodel <__main__.CompletionModel object at 0x7fefe1ccd770>, self <__main__.CompletionModel object at 0x7fefe1ccd808>
Traceback (most recent call last):
  File "model.py", line 59, in index
    raise ValueError("Model mismatch: parentmodel {}, self {}".format(parent.model(), self))
ValueError: Model mismatch: parentmodel <__main__.CompletionModel object at 0x7fefe1ccd770>, self <__main__.CompletionModel object at 0x7fefe1ccd808>

为什么会这样?我在我的模型中做错了什么,还是这是一个 Qt 错误?

我还尝试使用None 作为标记值,​​而我使用QModelIndex() 作为函数参数的默认值(尽管它不应该被修改,所以它不应该成为问题),但没有'没有帮助。

【问题讨论】:

  • 在 index() 方法中:是否可以使用名为“index”的局部变量与方法名称相同?
  • @yshurik 不错,但这应该不是问题。
  • 同样在 CompletionView 的 cinstructor 中,当我猜你想要 m2.init_data 时,你调用 m1.init_data
  • @yshurik 谢谢,已更正。

标签: qt model pyqt qtreeview qabstractitemmodel


【解决方案1】:

当您重置之前的模型时,可能在某处保留了一些引用,因此您会收到“模型不匹配”错误。

当我尝试您的示例时,我发现在重置之间将源模型设置为 None 可以消除错误:

    self.model().setSourceModel(m1)
    ...
    self.model().setSourceModel(None) # clear the current model
    ...
    self.model().setSourceModel(m2)

【讨论】:

  • 这确实是一个可行的解决方法。但是我仍然想知道这是否是我尚未发现的模型中的错误,或者它是否是 Qt 中的错误,我应该打开错误报告。虽然纯 C++ 测试用例会很方便。
  • @TheCompiler。我没有详细查看您的模型,但我看不出有任何明显的理由怀疑存在错误。这些问题在 PyQt 中一直出现,因为每个对象都有两个方面:Python 部分和 Qt 部分。很多时候,你需要明确地删除东西(并且以正确的顺序),而不是依靠垃圾收集器来为你整理。对于项目视图中的模型尤其如此(有关更多详细信息,请参阅setModel 的文档)。
【解决方案2】:

刚刚想起你为什么会出现这个问题。

其实是 Python 特有的带默认值的方法参数。 Python 的特殊之处在于这样的参数是 static_variable。 所以当第一次打电话时:

m1.index(1,2,parent_from_model_m1);

稍后只需调用:

m2.index(3,4)

然后对于第二次调用,“父级”将获得默认值 - 不是 QModelIndex() 而是 parent_from_model_m1(“父级”参数作为静态变量从第一次调用中记住 parent_from_model_m1)

解决方案应该很简单 - 删除默认值。

【讨论】:

  • 这不是默认参数在 python 中的工作方式。每次调用默认参数时都不会重置它们。执行类语句时,默认设置为 once。因此,每当调用 index(row, column) 时,parent 参数始终是 QModelIndexsame 实例。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-25
  • 2015-05-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多