下面的示例代码生成如下 GUI:
我想要的是仅在主要条目(图像中的 a、b 和 c)之前添加行计数,如手动编辑的图像所示:
如何在不更改数据/数据模型的情况下做到这一点? (我想我更喜欢插入一个全新的“虚拟列”,但我想一些只在展开/折叠图标/手柄上添加标签的黑客也可能会起作用......)
# https://gist.github.com/nbassler/342fc56c42df27239fa5276b79fca8e6
"""
Reworked code based on
http://trevorius.com/scrapbook/uncategorized/pyqt-custom-abstractitemmodel/
Adapted to Qt5 and fixed column/row bug.
TODO: handle changing data.
"""
import sys
from PyQt5 import QtCore, QtWidgets
class CustomNode(object):
def __init__(self, data):
self._data = data
if type(data) == tuple:
self._data = list(data)
if type(data) is str or not hasattr(data, '__getitem__'):
self._data = [data]
self._columncount = len(self._data)
self._children = []
self._parent = None
self._row = 0
def data(self, column):
if column >= 0 and column < len(self._data):
return self._data[column]
def columnCount(self):
return self._columncount
def childCount(self):
return len(self._children)
def child(self, row):
if row >= 0 and row < self.childCount():
return self._children[row]
def parent(self):
return self._parent
def row(self):
return self._row
def addChild(self, child):
child._parent = self
child._row = len(self._children)
self._children.append(child)
self._columncount = max(child.columnCount(), self._columncount)
class CustomModel(QtCore.QAbstractItemModel):
def __init__(self, nodes):
QtCore.QAbstractItemModel.__init__(self)
self._root = CustomNode(None)
for node in nodes:
self._root.addChild(node)
def rowCount(self, index):
if index.isValid():
return index.internalPointer().childCount()
return self._root.childCount()
def addChild(self, node, _parent):
if not _parent or not _parent.isValid():
parent = self._root
else:
parent = _parent.internalPointer()
parent.addChild(node)
def index(self, row, column, _parent=None):
if not _parent or not _parent.isValid():
parent = self._root
else:
parent = _parent.internalPointer()
if not QtCore.QAbstractItemModel.hasIndex(self, row, column, _parent):
return QtCore.QModelIndex()
child = parent.child(row)
if child:
return QtCore.QAbstractItemModel.createIndex(self, row, column, child)
else:
return QtCore.QModelIndex()
def parent(self, index):
if index.isValid():
p = index.internalPointer().parent()
if p:
return QtCore.QAbstractItemModel.createIndex(self, p.row(), 0, p)
return QtCore.QModelIndex()
def columnCount(self, index):
if index.isValid():
return index.internalPointer().columnCount()
return self._root.columnCount()
def data(self, index, role):
if not index.isValid():
return None
node = index.internalPointer()
if role == QtCore.Qt.DisplayRole:
return node.data(index.column())
return None
class MyTree():
"""
"""
def __init__(self):
self.items = []
# Set some random data:
for i in 'abc':
self.items.append(CustomNode(i))
self.items[-1].addChild(CustomNode(['d', 'e', 'f']))
self.items[-1].addChild(CustomNode(['g', 'h', 'i']))
self.tw = QtWidgets.QTreeView()
self.tw.setModel(CustomModel(self.items))
def add_data(self, data):
"""
TODO: how to insert data, and update tree.
"""
# self.items[-1].addChild(CustomNode(['1', '2', '3']))
# self.tw.setModel(CustomModel(self.items))
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
mytree = MyTree()
mytree.tw.show()
sys.exit(app.exec_())
好的,我想我已经解决了,基于向 QTreeView/QFileSystemModel 中的项目添加附加信息中的解决方案:
CustomModel
返回一个“被修改的”columnCount() 值,该值在数据的实际列数基础上增加 1 CustomModel.data()
中,检查我们是否在最后一个“被黑”/添加的列中,如果是,则确定该节点是否是“第一级”(除了将引用传递给持有者之外,找不到其他方法数据的,这里MyTree
,到CustomModel
- 这样我们就可以检索其中的数据MyTree.items
),如果是,则输出行号(否则不输出任何内容)CustomModel.data()
中,还可以通过section
检查我们是否在最后一个“被黑”/添加的列中,如果是,则输出#
.setModel()
在表视图上运行,之后,添加 .moveSection
以直观地将“黑客”/添加的列移动到其他位置 - 在本例中,在开头(最左边的位置),即列索引 0。 这样,我就得到了生成此 GUI 的代码:
...这正是我想要的(新的第一列现在有点太宽,但我想这并不难修复)。
应用引用答案中的原始解决方案很棘手,因为它们似乎只调用
columnCount()
,为此我收到错误“CustomModel.columnCount() 缺少 1 个必需的位置参数:'index'”。
# https://gist.github.com/nbassler/342fc56c42df27239fa5276b79fca8e6
'''
Reworked code based on
http://trevorius.com/scrapbook/uncategorized/pyqt-custom-abstractitemmodel/
Adapted to Qt5 and fixed column/row bug.
TODO: handle changing data.
'''
import sys
from PyQt5 import QtCore, QtWidgets
class CustomNode(object):
def __init__(self, data):
self._data = data
if type(data) == tuple:
self._data = list(data)
if type(data) is str or not hasattr(data, '__getitem__'):
self._data = [data]
self._columncount = len(self._data)
self._children = []
self._parent = None
self._row = 0
def data(self, column):
if column >= 0 and column < len(self._data):
return self._data[column]
def columnCount(self):
return self._columncount
def childCount(self):
return len(self._children)
def child(self, row):
if row >= 0 and row < self.childCount():
return self._children[row]
def parent(self):
return self._parent
def row(self):
return self._row
def addChild(self, child):
child._parent = self
child._row = len(self._children)
self._children.append(child)
self._columncount = max(child.columnCount(), self._columncount)
class CustomModel(QtCore.QAbstractItemModel):
def __init__(self, dataparent, nodes):
QtCore.QAbstractItemModel.__init__(self)
self._root = CustomNode(None)
self._dataparent = dataparent
for node in nodes:
self._root.addChild(node)
def rowCount(self, index):
if index.isValid():
return index.internalPointer().childCount()
return self._root.childCount()
def addChild(self, node, _parent):
if not _parent or not _parent.isValid():
parent = self._root
else:
parent = _parent.internalPointer()
parent.addChild(node)
def index(self, row, column, _parent=None):
if not _parent or not _parent.isValid():
parent = self._root
else:
parent = _parent.internalPointer()
if not QtCore.QAbstractItemModel.hasIndex(self, row, column, _parent):
return QtCore.QModelIndex()
child = parent.child(row)
if child:
return QtCore.QAbstractItemModel.createIndex(self, row, column, child)
else:
return QtCore.QModelIndex()
def parent(self, index):
if index.isValid():
p = index.internalPointer().parent()
if p:
return QtCore.QAbstractItemModel.createIndex(self, p.row(), 0, p)
return QtCore.QModelIndex()
def columnCount(self, index):
hack = 1 # https://stackoverflow.com/q/46835109
if index.isValid():
return index.internalPointer().columnCount() +hack
return self._root.columnCount() +hack
def data(self, index, role):
extra = False
if not index.isValid():
return None
extra = index.column() == self.columnCount(index.parent()) - 1 # https://stackoverflow.com/q/46835109 ; no +hack here, else no match!
node = index.internalPointer()
is_first_level = (node in self._dataparent.items)
if role == QtCore.Qt.DisplayRole:
if extra and is_first_level:
return "🚣{}".format(index.row()+1) # 1-based count
else:
return node.data(index.column())
return None
def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole): # https://stackoverflow.com/q/64287713
if (orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole):
if section == self.columnCount(QtCore.QModelIndex()) - 1:
return '#'
return super().headerData(section, orientation, role)
class MyTree():
'''
'''
def __init__(self):
self.items = []
# Set some random data:
for i in 'abc':
self.items.append(CustomNode(i))
self.items[-1].addChild(CustomNode(['d', 'e', 'f']))
self.items[-1].addChild(CustomNode(['g', 'h', 'i']))
self.tw = QtWidgets.QTreeView()
self.tw.setModel(CustomModel(self, self.items))
last_column_idx = self.tw.model().columnCount(QtCore.QModelIndex()) - 1
self.tw.header().moveSection(last_column_idx, 0) # https://stackoverflow.com/q/46835109 "Moves the section at visual index `from` to occupy visual index `to`"
def add_data(self, data):
'''
TODO: how to insert data, and update tree.
'''
# self.items[-1].addChild(CustomNode(['1', '2', '3']))
# self.tw.setModel(CustomModel(self.items))
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
mytree = MyTree()
mytree.tw.show()
sys.exit(app.exec_())