子类化 QAbstractItemModel 以将嵌套的 dict 数据显示为树

问题描述

我正在尝试使用嵌套字典来实现树状结构来存储我的数据并使用 QTreeView 显示它。 对我来说,关键部分是跟踪对嵌套 dict 结构或 QTreeView 中的数据所做的更改。我尝试对 qabstractitemmodel 进行子类化,但无法弄清楚如何将 QTreeView 中所做的更改与我的嵌套 dict 结构同步,唯一有效的是在每次更改时重新填充整个树......是否可以在不重建的情况下实现这样的事情每次进行更改时都会生成一棵树?

所需的行为是这样的: 嵌套dict中的更改数据->更改会自动显示在QTreeView中。

我发现了类似的问题,但用 QTreeWidget 代替:QTreeWidget to Mirror python Dictionary

但是,这种方法也需要在每次更改时重新填充树...

嵌套字典(树数据)看起来像这样:

root_node = {
            node1: { leaf1: data,leaf2: data}
            node2: { node3: { leaf3: data,leaf4: data }}
       }

UPD:添加了我目前得到的代码

from PyQt5.QtCore import QModelIndex,Qt,qabstractitemmodel


class TreeNode(object):
    def __init__(self,data,parent=None):
        self.parent_node = parent
        self.node_data = data  # it's also []
        self.child_nodes = []

    def child(self,row):
        return self.child_nodes[row]

    def child_count(self):
        return len(self.child_nodes)

    def child_number(self):
        if self.parent_node is not None:
            return self.parent_node.child_nodes.index(self)
        return 0

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

    def data(self,column):
        return self.node_data[column]

    def insert_children(self,position,count,columns):
        if position < 0 or position > len(self.child_nodes):
            return False

        for row in range(count):
            data = [None for v in range(columns)]
            node = TreeNode(data,self)
            self.child_nodes.insert(position,node)

        return True

    def append_child_by_node(self,node):
        node.parent_node = self
        self.child_nodes.append(node)

    def append_child_by_data(self,data):
        self.child_nodes.append(TreeNode(data,self))

    def insert_columns(self,columns):
        if position < 0 or position > len(self.node_data):
            return False

        for column in range(columns):
            self.node_data.insert(position,None)

        for child in self.child_nodes:
            child.insert_columns(position,columns)

        return True

    def parent(self):
        return self.parent_node

    def remove_children(self,count):
        if position < 0 or position + count > len(self.child_nodes):
            return False

        for row in range(count):
            self.child_nodes.pop(position)

        return True

    def remove_columns(self,columns):
        if position < 0 or position + columns > len(self.node_data):
            return False

        for column in range(columns):
            self.node_data.pop(position)

        for child in self.child_nodes:
            child.remove_columns(position,columns)

        return True

    def set_data(self,column,value):
        if column < 0 or column >= len(self.node_data):
            return False

        self.node_data[column] = value

        return True


class TreeModel(qabstractitemmodel):
    def __init__(self,parent=None,nested_dict=dict):
        super(TreeModel,self).__init__(parent)
        self.root_node = TreeNode(["Name","Type"])
        self.update_tree(self.root_node,nested_dict)

        print(self.root_node.child_count())

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

    def data(self,index,role):
        if not index.isValid():
            return None

        if role != Qt.displayRole and role != Qt.EditRole:
            return None

        node = self.get_node(index)
        return node.data(index.column())

def flags(self,index):
    if not index.isValid():
        return 0

    return Qt.ItemIsEnabled | Qt.ItemIsSelectable

def get_node(self,index):
    if index.isValid():
        node = index.internalPointer()
        if node:
            return node

    return self.root_node

def headerData(self,section,orientation,role=Qt.displayRole):
    if orientation == Qt.Horizontal and role == Qt.displayRole:
        return self.root_node.data(section)

    return None

def index(self,row,parent=QModelIndex()):
    if parent.isValid() and parent.column() != 0:
        return QModelIndex()

    parent_node = self.get_node(parent)
    child_node = parent_node.child(row)
    if child_node:
        return self.createIndex(row,child_node)
    else:
        return QModelIndex()

def insertColumns(self,columns,parent=QModelIndex()):
    self.beginInsertColumns(parent,position + columns - 1)
    success = self.root_node.insert_columns(position,columns)
    self.endInsertColumns()

    return success

def insertRows(self,rows,parent=QModelIndex()):
    parent_node = self.get_node(parent)
    self.beginInsertRows(parent,position + rows - 1)
    success = parent_node.insert_children(position,self.root_node.column_count())
    self.endInsertRows()

    return success

def parent(self,index):
    if not index.isValid():
        return QModelIndex()

    child_node = self.get_node(index)
    parent_node = child_node.parent()

    if parent_node == self.root_node:
        return QModelIndex()

    return self.createIndex(parent_node.child_number(),parent_node)

def removeColumns(self,parent=QModelIndex()):
    self.beginRemoveColumns(parent,position + columns - 1)
    success = self.root_node.remove_columns(position,columns)
    self.endRemoveColumns()

    if self.root_node.column_count() == 0:
        self.removeRows(0,self.rowCount())

    return success

def removeRows(self,parent=QModelIndex()):
    parent_node = self.get_node(parent)

    self.beginRemoveRows(parent,position + rows - 1)
    success = parent_node.remove_children(position,rows)
    self.endRemoveRows()

    return success

def rowCount(self,parent=QModelIndex()):
    parent_node = self.get_node(parent)

    return parent_node.child_count()

def setData(self,value,role=Qt.EditRole):
    if role != Qt.EditRole:
        return False

    node = self.get_node(index)
    result = node.set_data(index.column(),value)

    if result:
        self.dataChanged.emit(index,index)

    return result

def setHeaderData(self,role=Qt.EditRole):
    if role != Qt.EditRole or orientation != Qt.Horizontal:
        return False

    result = self.root_node.set_data(section,value)
    if result:
        self.headerDataChanged.emit(orientation,section)

    return result

def update_tree(self,parent,nested_dict):
    self.layoutChanged.emit()
    parent.child_nodes.clear()
    for k,v in nested_dict.items():
        if isinstance(v,dict):
            parent.append_child_by_data([k[0] if isinstance(k,tuple) else k,None])
            self.update_tree(parent.child(parent.child_count() - 1),v)
        else:
            parent.append_child_by_data([k,None])

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)