使用自定义模型编辑 QTreeView 中的第二列不显示编辑器

Editing Second Column in QTreeView with custom model Doesn't show editor

我正在尝试制作一个带有自定义模型的 QTreeView,但我一直想不出如何让默认编辑器显示在第二列中。第一列显示正常

我的自定义模型中的基础数据是嵌套的 OrderedDicts。基 OrderedDict 是一个特殊的子类 TupleKeyedOrderedDict,重写了 __getitem__ 方法以检查键是否为元组。如果键是元组,则递归迭代数据结构,直到没有更多键为止。

下面的示例是一个工作示例,其中第二列没有绘制编辑器,即使可以更改数据。

import sys
from collections import OrderedDict
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtCore import Qt

class TupleKeyedOrderedDict(OrderedDict):
    def __init__(self, *args, **kwargs):
        super().__init__(sorted(kwargs.items()))

    def __getitem__(self, key):
        if isinstance(key, tuple):
            item = self
            for k in key:
                if item != ():
                    item = item[k]
            return item
        else:
            return super().__getitem__(key)

    def __setitem__(self, key, value):
        if isinstance(key, tuple):
            item = self
            previous_item = None
            for k in key:
                if item != ():
                    previous_item = item
                    item = item[k]
            previous_item[key[-1]] = value
        else:
            return super().__setitem__(key, value)

class SettingsModel(QtCore.QAbstractItemModel):
    def __init__(self, data, parent=None):
        super().__init__(parent)
        self.root = data
        self.my_index = {}   # Needed to stop garbage collection

    def index(self, row, column, parent):
        if not self.hasIndex(row, column, parent):
            return QtCore.QModelIndex()
        if parent.isValid():
            index_pointer = parent.internalPointer()
            parent_dict = self.root[index_pointer]
        else:
            parent_dict = self.root
            index_pointer = ()
        row_key = list(parent_dict.keys())[row]
        child_pointer = (index_pointer, row_key)
        try:
            child_pointer = self.my_index[child_pointer]
        except KeyError:
            self.my_index[child_pointer] = child_pointer
        index = self.createIndex(row, column, child_pointer)
        return index

    def get_row(self, key):
        if key:
            parent = key[:-1]
            if not parent:
                return 0
            return list(self.root[parent].keys()).index(key[-1])
        else:
            return 0

    def parent(self, index):
        if not index.isValid():
            return QtCore.QModelIndex()
        child_key_list = index.internalPointer()
        if child_key_list:
            parent_key_list = child_key_list[:-1]
            try:
                parent_key_list = self.my_index[parent_key_list]
            except KeyError:
                self.my_index[parent_key_list] = parent_key_list
            return self.createIndex(self.get_row(parent_key_list), 0,
                                    parent_key_list)
        else:
            return QtCore.QModelIndex()

    def rowCount(self, parent):
        if parent.column() > 0:
            return 0    # only keys have children, not values
        if parent.isValid():
            indexPtr = parent.internalPointer()
            parentValue = self.root[indexPtr]
            if isinstance(parentValue, OrderedDict):
                return len(self.root[indexPtr])
            else:
                return 0
        else:
            return len(self.root)

    def columnCount(self, parent):
        return 2  # Key & value

    def data(self, index, role):
        if not index.isValid():
            return None
        if role in (QtCore.Qt.DisplayRole, QtCore.Qt.EditRole):
            indexPtr = index.internalPointer()
            if index.column() == 1:    # Column 1, send the value
                return self.root[indexPtr]
            else:                   # Column 0, send the key
                if indexPtr:
                    return indexPtr[-1]
                else:
                    return None
        else:  # Not display or Edit
            return None

    def setData(self, index, value, role):
        pointer = self.my_index[index.internalPointer()]
        self.root[pointer] = value
        self.dataChanged.emit(index, index)
        return True

    def flags(self, index):
        if not index.isValid()
            return 0
        return Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    data = TupleKeyedOrderedDict(**{'1': OrderedDict({'sub': 'b'}), '2': OrderedDict({'subsub': '3'})})

    model = SettingsModel(data)
    tree_view = QtWidgets.QTreeView()
    tree_view.setModel(model)
    tree_view.show()
    sys.exit(app.exec_())

问题是在 SettingsModel

index 方法中解包元组
import sys
from collections import OrderedDict
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtCore import Qt

class TupleKeyedOrderedDict(OrderedDict):
    def __init__(self, *args, **kwargs):
        super().__init__(sorted(kwargs.items()))

    def __getitem__(self, key):
        if isinstance(key, tuple):
            item = self
            for k in key:
                if item != ():
                    item = item[k]
            return item
        else:
            return super().__getitem__(key)

    def __setitem__(self, key, value):
        if isinstance(key, tuple):
            item = self
            previous_item = None
            for k in key:
                if item != ():
                    previous_item = item
                    item = item[k]
            previous_item[key[-1]] = value
        else:
            return super().__setitem__(key, value)

class SettingsModel(QtCore.QAbstractItemModel):
    def __init__(self, data, parent=None):
        super().__init__(parent)
        self.root = data
        self.my_index = {}   # Needed to stop garbage collection

    def index(self, row, column, parent):
        if not self.hasIndex(row, column, parent):
            return QtCore.QModelIndex()
        if parent.isValid():
            index_pointer = parent.internalPointer()
            parent_dict = self.root[index_pointer]
        else:
            parent_dict = self.root
            index_pointer = ()
        row_key = list(parent_dict.keys())[row]
        child_pointer = (*index_pointer, row_key)
        try:
            child_pointer = self.my_index[child_pointer]
        except KeyError:
            self.my_index[child_pointer] = child_pointer
        index = self.createIndex(row, column, child_pointer)
        return index

    def get_row(self, key):
        if key:
            parent = key[:-1]
            if not parent:
                return 0
            return list(self.root[parent].keys()).index(key[-1])
        else:
            return 0

    def parent(self, index):
        if not index.isValid():
            return QtCore.QModelIndex()
        child_key_list = index.internalPointer()
        if child_key_list:
            parent_key_list = child_key_list[:-1]
            try:
                parent_key_list = self.my_index[parent_key_list]
            except KeyError:
                self.my_index[parent_key_list] = parent_key_list
            return self.createIndex(self.get_row(parent_key_list), 0,
                                    parent_key_list)
        else:
            return QtCore.QModelIndex()

    def rowCount(self, parent):
        if parent.column() > 0:
            return 0    # only keys have children, not values
        if parent.isValid():
            indexPtr = parent.internalPointer()
            parentValue = self.root[indexPtr]
            if isinstance(parentValue, OrderedDict):
                return len(self.root[indexPtr])
            else:
                return 0
        else:
            return len(self.root)

    def columnCount(self, parent):
        return 2  # Key & value

    def data(self, index, role):
        if not index.isValid():
            return None
        if role in (QtCore.Qt.DisplayRole, QtCore.Qt.EditRole):
            indexPtr = index.internalPointer()
            if index.column() == 1:    # Column 1, send the value
                return self.root[indexPtr]
            else:                   # Column 0, send the key
                if indexPtr:
                    return indexPtr[-1]
                else:
                    return None
        else:  # Not display or Edit
            return None

    def setData(self, index, value, role):
        pointer = self.my_index[index.internalPointer()]
        self.root[pointer] = value
        self.dataChanged.emit(index, index)
        return True

    def flags(self, index):
        if not index.isValid():
            return 0
        return Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    data = TupleKeyedOrderedDict(**{'1': OrderedDict({'sub': 'b'}), '2': OrderedDict({'subsub': '3'})})

    model = SettingsModel(data)
    tree_view = QtWidgets.QTreeView()
    tree_view.setModel(model)
    tree_view.show()
    sys.exit(app.exec_())