跨越多列,使用 QTreeView 和 QAbstractItemModel

alm*_*est 2 python pyqt qtreeview qabstractitemmodel

我想制作一个 TreeView,其中顶级条目跨越所有列(例如它们有一行),而子项跨越多个列(例如它们有多行。)

我试图用 QTreeView.setFirstColumnSpanned 来完成这个。但是,我真的不知道在何处/何时调用它(也许我一开始就完全错误。)

我想也许我可以在我的视图中创建一个函数,该函数将在视图填充后调用,以检查任何需要更新其跨度的项目。

但是,我找不到任何可以连接的信号来帮助我做到这一点(我尝试了几个,但似乎都没有触发。)

我还尝试在我的模型中重新实现 insertRows 和/或在我的视图中重新实现 rowsInserted,但它们似乎从未被调用过。

这是我正在做的极其简化的版本:

class MyModel(QtCore.QAbstractItemModel):
    def __init__(self, top_level_nodes):
        QtCore.QAbstractItemModel.__init__(self)
        self.top_level_nodes = top_level_nodes
        self.columns = 5

    def columnCount(self, parent):
        return self.columns

    def rowCount(self, parent):
        total = len(self.top_level_nodes)
        for node in self.top_level_nodes:
            total += len(node.subnodes)
        return total

    def data(self, index, role):

        if not index.isValid():
            return QtCore.QVariant()

        if role == QtCore.Qt.DisplayRole:
            obj = index.internalPointer()
            return obj.name

        return QtCore.QVariant()

    def index(self, row, column, parent):
        if not parent.isValid():
            if row > (len(self.top_level_nodes) - 1):
                return QtCore.QModelIndex()

            return self.createIndex(row, column, self.top_level_nodes[row])

        return QtCore.QModelIndex()

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

        node = index.internalPointer()
        if node.parent is None:
            return QtCore.QModelIndex()

        else:
            return self.createIndex(node.parent.row, 0, node.parent)

class FakeEntry(object):
    def __init__(self, name, row, children=[]):
        self.parent = None
        self.row = row
        self.name = 'foo'
        self.subnodes = children

class MainWindow(QtGui.QWidget):
    def __init__(self):
        QtGui.QWidget.__init__(self)

        self.resize(800, 600)

        self.layout = QtGui.QVBoxLayout(self)
        self.tree_view = QtGui.QTreeView(self)
        self.layout.addWidget(self.tree_view)

        entry = FakeEntry('foo', 0, children=[])
        self.model = MyModel([entry])
        self.tree_view.setModel(self.model)

def main():
    app = QtGui.QApplication(sys.argv)
    frame = MainWindow()
    frame.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()
Run Code Online (Sandbox Code Playgroud)

这段代码运行良好。我需要知道的是:

如何让 FakeEntry 创建的条目跨越所有 5 列而不是创建 5 个相同的单元格?

其他问题:

  • 我可以重新实现 QAbstractItemModel.insertRows 或 QTreeView.rowsInserted 吗?是否有一个我缺少的“技巧”来执行此操作,这会导致我的代码选择默认实现吗?
  • 一旦模型填充数据并附加到视图,实际上处理在视图中创建行/列的操作是什么?

Chr*_*ris 5

如果我添加调用这个工作对我来说setFirstColumnSpanned在以后setModel()的调用MainWindow.__init__()

class MainWindow(QtGui.QWidget):
    def __init__(self):
        QtGui.QWidget.__init__(self)

        self.resize(800, 600)

        self.layout = QtGui.QVBoxLayout(self)
        self.tree_view = QtGui.QTreeView(self)
        self.layout.addWidget(self.tree_view)

        entry = FakeEntry('foo', 0, children=[])
        self.model = MyModel([entry])
        self.tree_view.setModel(self.model)
        self.tree_view.setFirstColumnSpanned(0, QtCore.QModelIndex(), True)
Run Code Online (Sandbox Code Playgroud)

我相信视图会在内部跟踪跨列,与模型分开。但它会在每次setModel()调用时重置跨度信息。所以,你应该setFirstColumnSpanned()在调用setModel()视图之后调用。如果您的模型正在使用 进行延迟加载fetchMore(),则您必须想出某种方式来通知视图它应该在模型加载新数据后更新跨度。

关于在视图中创建行/列的实际处理,我认为这是模型和视图之间的伙伴关系。一旦视图有了模型集,它就会开始向模型提出各种问题。对于最顶层的父项(无效索引),有多少列?很酷,有 5 行。有多少行?好的, 1. 仅根据该信息,视图的标题就可以开始自行配置。在某个时刻,视图遍历模型中的行、列和子项,并针对每个数据角色依次向每个索引询问其数据。然后它可以开始创建代理以在视图中的适当位置显示该数据。