代码之家  ›  专栏  ›  技术社区  ›  Vince W.

如何在具有不同层次列计数的模型的QTreeView中显示适当数量的列

  •  0
  • Vince W.  · 技术社区  · 6 年前

    在下面的代码示例中,我使用一组顶级项目填充项目模型,这些项目包含我可以使用QTreeView查看和编辑的键值属性。

    QAbstractItemModel::columnCount

    但是,使用下面的代码,如果我将列计数作为分层依赖属性返回(在本例中 root->children 有1列, root->child->children 有2列)则视图将仅显示1列。

    enter image description here

    印刷 node.columnCount() Item 在展开其中一个项后,类节点实际上返回columnCount=2。

    如果我总是返回2的话 model.columnCount 函数,则视图将正确显示两列。

    enter image description here

    无论是层次结构还是我只是做错了什么,都需要在视图中始终返回所需的列数吗?如果是,那又怎样?为其子级具有不同列数的父级返回多个列只是为了使视图正常工作,这感觉一定是错误的。

    import sys
    import typing
    from PyQt5 import QtCore, QtWidgets
    
    
    class Node:
        def __init__(self, parent=None):
            self.parent = parent  # type: Node
            self.name : str
    
        def children(self) -> list:
            return None
    
        def hasChildren(self):
            return bool(self.children())
    
        def getData(self, index: QtCore.QModelIndex):
            if index.column() == 0:
                return self.name
    
        def setData(self, val, index: QtCore.QModelIndex):
            if index.column() == 0:
                self.name = val
    
        def columnCount(self):
            return 1
    
        def rowCount(self):
            children = self.children()
            return 0 if not children else len(children)
    
        def flags(self, index: QtCore.QModelIndex):
            if index.column() == 0:
                return (QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEditable)
            else:
                return QtCore.Qt.NoItemFlags
    
    
    class Property(Node):
        def __init__(self, parent, label, value):
            super().__init__(parent)
            self.label = label
            self.value = value
    
        def getData(self, index: QtCore.QModelIndex):
            col = index.column()
            if col == 0:
                return self.label
            elif col == 1:
                return self.value
    
        def setData(self, val, index: QtCore.QModelIndex):
            if index.column() == 1:
                self.value = val
    
        def flags(self, index: QtCore.QModelIndex):
            col = index.column()
            if col == 0:
                return QtCore.Qt.ItemIsEnabled
            elif col == 1:
                return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEditable
    
    
    class Item(Node):
        def __init__(self, parent):
            super().__init__(parent)
            self.name = 'Item'
            self.p1 = Property(self, 'string', 'text')
            self.p2 = Property(self, 'float', 1.2)
    
        def children(self):
            return [self.p1, self.p2]
    
        def columnCount(self):
            return 2
    
    
    class Root(Node):
        def __init__(self):
            super().__init__(parent=None)
            self._children = list()
    
        def children(self):
            return self._children
    
    
    class Model(QtCore.QAbstractItemModel):
        def __init__(self):
            super().__init__()
            self.root = Root()
    
        def index(self, row: int, column: int, parent: QtCore.QModelIndex = ...) -> QtCore.QModelIndex:
            if not self.hasIndex(row, column, parent):
                return QtCore.QModelIndex()
    
            node = parent.internalPointer() if parent.isValid() else self.root
            if node.children:
                return self.createIndex(row, column, node.children()[row])
            else:
                return QtCore.QModelIndex()
    
        def parent(self, child: QtCore.QModelIndex) -> QtCore.QModelIndex:
            if not child.isValid():
                return QtCore.QModelIndex()
    
            node = child.internalPointer()  # type: Node
    
            if node.parent and node.parent.parent:
                row = node.parent.parent.children().index(node.parent)
                return self.createIndex(row, 0, node.parent)
            else:
                return QtCore.QModelIndex()
    
        def rowCount(self, parent: QtCore.QModelIndex = ...) -> int:
            node = parent.internalPointer() if parent.isValid() else self.root
            children = node.children()
            return len(children) if children else 0
    
        def columnCount(self, parent: QtCore.QModelIndex = ...) -> int:
            node = parent.internalPointer() if parent.isValid() else self.root
            print(f'{node.__class__.__name__} column count: ', node.columnCount())  # shows that column count 2 is returned, when items are expanded
            # return 2  # 2nd column only shows up if I just always return 2
            return node.columnCount()  # view only shows 1 columns
    
        def hasChildren(self, parent: QtCore.QModelIndex = ...) -> bool:
            node = parent.internalPointer() if parent.isValid() else self.root
            return node.hasChildren()
    
        def data(self, index: QtCore.QModelIndex, role: int = ...):
            if index.isValid() and role in (QtCore.Qt.DisplayRole, QtCore.Qt.EditRole):
                node = index.internalPointer()  # type: Node
                return node.getData(index)
            else:
                return None
    
        def setData(self, index: QtCore.QModelIndex, value: typing.Any, role: int = ...) -> bool:
            if role in (QtCore.Qt.EditRole,):
                node = index.internalPointer()  # type: Node
                node.setData(value, index)
                self.dataChanged.emit(index, index)
                return True
            else:
                return False
    
        def flags(self, index: QtCore.QModelIndex):
            node = index.internalPointer() if index.isValid() else self.root
            return node.flags(index)
    
        def appendRow(self, item):
            row = len(self.root.children())
            self.beginInsertRows(QtCore.QModelIndex(), row, row)
            self.root.children().append(item)
            self.endInsertRows()
    
    
    class TreeView(QtWidgets.QTreeView):
        def __init__(self, parent=None):
            super(TreeView, self).__init__(parent)
            self._model = Model()
            self.setModel(self._model)
            self.setSelectionMode(self.ExtendedSelection)
            # self.setDropIndicatorShown(False)
            self.setEditTriggers(self.DoubleClicked | self.SelectedClicked | self.EditKeyPressed)
    
        def model(self) -> Model:
            return self._model
    
    sys.excepthook = sys.__excepthook__
    app = QtWidgets.QApplication(sys.argv)
    widget = TreeView()
    model = widget.model()
    for i in range(2):
        model.appendRow(Item(model.root))
    widget.show()
    widget.setAttribute(QtCore.Qt.WA_DeleteOnClose)
    sys.exit(app.exec_())
    
    2 回复  |  直到 6 年前
        1
  •  2
  •   eyllanesc Yonghwan Shin    6 年前

    看起来文档不清晰,并且与实现不完全匹配,在实现中,视图中的列数取决于 水平的 QHeaderView ,以及 水平的 QHeaderView 使用根目录中不可见项的列数,即列数应由 Root() ,自 根() columnCount() 默认情况下,它的值为1(尽管对我来说 节点的值必须为0,并且 children() 必须返回空列表),因此解决方案在根目录中设置为2 .

    import sys
    import typing
    from PyQt5 import QtCore, QtWidgets
    
    
    class Node:
        def __init__(self, parent=None):
            self.parent = parent  # type: Node
            self.name : str
    
        def children(self) -> list:
            return list()
    
        def hasChildren(self):
            return bool(self.children())
    
        def getData(self, index: QtCore.QModelIndex):
            if index.column() == 0:
                return self.name
    
        def setData(self, val, index: QtCore.QModelIndex):
            if index.column() == 0:
                self.name = val
    
        def columnCount(self):
            return 0
    
        def rowCount(self):
            children = self.children()
            return len(children)
    
        def flags(self, index: QtCore.QModelIndex):
            if index.column() == 0:
                return (QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEditable)
            else:
                return QtCore.Qt.NoItemFlags
    
    
    class Property(Node):
        def __init__(self, parent, label, value):
            super().__init__(parent)
            self.label = label
            self.value = value
    
        def getData(self, index: QtCore.QModelIndex):
            col = index.column()
            if col == 0:
                return self.label
            elif col == 1:
                return self.value
    
        def setData(self, val, index: QtCore.QModelIndex):
            if index.column() == 1:
                self.value = val
    
        def flags(self, index: QtCore.QModelIndex):
            col = index.column()
            if col == 0:
                return QtCore.Qt.ItemIsEnabled
            elif col == 1:
                return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEditable
    
        def columnCount(self):
            return 1
    
    
    class Item(Node):
        def __init__(self, parent):
            super().__init__(parent)
            self.name = 'Item'
            self.p1 = Property(self, 'string', 'text')
            self.p2 = Property(self, 'float', 1.2)
    
        def children(self):
            return [self.p1, self.p2]
    
        def columnCount(self):
            return 2
    
    
    class Root(Node):
        def __init__(self):
            super().__init__(parent=None)
            self._children = list()
    
        def children(self):
            return self._children
    
        def columnCount(self):
            return 2
    
    
    class Model(QtCore.QAbstractItemModel):
        def __init__(self):
            super().__init__()
            self.root = Root()
    
        def index(self, row: int, column: int, parent: QtCore.QModelIndex = ...) -> QtCore.QModelIndex:
            if not self.hasIndex(row, column, parent):
                return QtCore.QModelIndex()
    
            node = parent.internalPointer() if parent.isValid() else self.root
            if node.children:
                return self.createIndex(row, column, node.children()[row])
            else:
                return QtCore.QModelIndex()
    
        def parent(self, child: QtCore.QModelIndex) -> QtCore.QModelIndex:
            if not child.isValid():
                return QtCore.QModelIndex()
    
            node = child.internalPointer()  # type: Node
    
            if node.parent and node.parent.parent:
                row = node.parent.parent.children().index(node.parent)
                return self.createIndex(row, 0, node.parent)
            else:
                return QtCore.QModelIndex()
    
        def rowCount(self, parent: QtCore.QModelIndex = ...) -> int:
            node = parent.internalPointer() if parent.isValid() else self.root
            children = node.children()
            return len(children) if children else 0
    
        def columnCount(self, parent: QtCore.QModelIndex = ...) -> int:
            node = parent.internalPointer() if parent.isValid() else self.root
            print(f'{node.__class__.__name__} column count: ', node.columnCount())  # shows that column count 2 is returned, when items are expanded
            # return 2  # 2nd column only shows up if I just always return 2
            return node.columnCount()  # view only shows 1 columns
    
        def hasChildren(self, parent: QtCore.QModelIndex = ...) -> bool:
            node = parent.internalPointer() if parent.isValid() else self.root
            return node.hasChildren()
    
        def data(self, index: QtCore.QModelIndex, role: int = ...):
            if index.isValid() and role in (QtCore.Qt.DisplayRole, QtCore.Qt.EditRole):
                node = index.internalPointer()  # type: Node
                return node.getData(index)
            else:
                return None
    
        def setData(self, index: QtCore.QModelIndex, value: typing.Any, role: int = ...) -> bool:
            if role in (QtCore.Qt.EditRole,):
                node = index.internalPointer()  # type: Node
                node.setData(value, index)
                self.dataChanged.emit(index, index)
                return True
            else:
                return False
    
        def flags(self, index: QtCore.QModelIndex):
            node = index.internalPointer() if index.isValid() else self.root
            return node.flags(index)
    
        def appendRow(self, item):
            row = len(self.root.children())
            self.beginInsertRows(QtCore.QModelIndex(), row, row)
            self.root.children().append(item)
            self.endInsertRows()
    
    
    class TreeView(QtWidgets.QTreeView):
        def __init__(self, parent=None):
            super(TreeView, self).__init__(parent)
            self._model = Model()
            self.setModel(self._model)
            self.setSelectionMode(self.ExtendedSelection)
            # self.setDropIndicatorShown(False)
            self.setEditTriggers(self.DoubleClicked | self.SelectedClicked | self.EditKeyPressed)
    
        def model(self) -> Model:
            return self._model
    
    sys.excepthook = sys.__excepthook__
    app = QtWidgets.QApplication(sys.argv)
    widget = TreeView()
    model = widget.model()
    for i in range(2):
        model.appendRow(Item(model.root))
    widget.show()
    widget.setAttribute(QtCore.Qt.WA_DeleteOnClose)
    sys.exit(app.exec_())
    
        2
  •  1
  •   Vince W.    6 年前

    columnCount()

    class Root(Node):
        ...
        def columnCount():
            return 2
    
    class Property(Node):
        ...
        def columnCount():
            return 1
    

    Property 节点将不显示第二列的数据,因为视图由 columnCount() 只有一列