(Py)Qt:QSortFilterProxyModel使用来自错误模型的父调用index()

时间:2014-03-07 20:19:22

标签: qt model pyqt qtreeview qabstractitemmodel

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

注意如果我的父母错了,我会在ValueError中提出index()

现在当我执行我的示例脚本(粘贴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()作为函数参数的默认值(虽然它不应该被修改,所以它应该不是问题) ,这没有帮助。

2 个答案:

答案 0 :(得分:2)

当您重置之前,可能有一些参考文献被保留在上一个模型的位置,因此您会收到“模型不匹配”错误。

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

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

答案 1 :(得分:-2)

记得你遇到这个问题的原因。

实际上,它是具有默认值的方法参数的Python特殊性。 Python特殊性是这样的参数是static_variable。 所以当第一次打电话时:

m1.index(1,2,parent_from_model_m1);

以后再打电话:

m2.index(3,4)

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

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