看起来文档不清晰,并且与实现不完全匹配,在实现中,视图中的列数取决于
水平的
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_())