В приведенном ниже примере кода я заполняю модель элемента набором элементов верхнего уровня, которые содержат свойства значения ключа, которые я могу просматривать и редактировать с помощью QTreeView.
Глядя на документацию Qt для QAbstractItemModel :: columnCount , он говорит, что это должно вернуть число столбцов для дочерних элементов данного родителя, что означает, что это должно быть иерархически зависимое свойство.
Однако, используя приведенный ниже код, если я верну количество столбцов в качестве иерархически зависимого свойства (в данном случае root->children
имеет 1 столбец, root->child->children
имеет 2 столбца), то представление будет отображать только 1 столбец.
Печать node.columnCount()
(см. Код) фактически покажет, что узлы класса Item
действительно возвращают columnCount = 2 после развертывания одного из элементов.
Если я просто всегда возвращаю 2 для функции model.columnCount
, то представление будет правильно отображать оба столбца.
Требуется ли это, чтобы всегда возвращать желаемое количество столбцов в представлении, независимо от иерархии, или я просто что-то делаю неправильно, и если да, то что? Возвращая количество столбцов для родителя, чьи дети имеют разное количество столбцов, просто чтобы заставить представление работать должным образом, кажется, что это должно быть неправильно.
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_())