【问题标题】:Display data from QAbstractTableModel in QTreeView在 QTreeView 中显示来自 QAbstractTableModel 的数据
【发布时间】:2015-02-03 04:13:04
【问题描述】:

短版

我正在尝试在QTreeView 中显示来自QAbstracctTableModel 的数据。当我这样做时,我最终将 整个表 显示为每个节点的子节点(以及孙子节点,等等)。如何显示抽象表模型的树视图?

详情

我正在尝试在QAbstractTableModel 中显示QTreeView 中的一些数据。在Model-View Tutorial 中,在展示了一个示例QAbstractTableModel 之后,它看起来就像用QTreeView 替换QTableView 一样简单:

您可以将上面的示例转换为带有树的应用程序 看法。 只需将 QTableView 替换为 QTreeView,结果是 读/写树。无需对模型进行任何更改。

当我进行这个替换时,我最终会显示一个树,但是如果我单击任何图标来展开它(这应该什么都不做,因为没有内置层次结构),Python 会崩溃并显示 @987654332 @。这个has been brought up before ],但没有可行的解决方案。

为了尝试解决此问题,我在QAbstractTableModel 子类中重新实现了索引函数(请参阅下面的完整工作示例)。这导致了一种非常不同类型的错误。也就是说,树中的每个节点现在都包含整个表作为数据。无论我点击多少次,整个表格都会显示出来。像这样:

我似乎陷入了某种递归的噩梦,不知道如何逃脱。下面的相关问题表明我可能不得不去QAbstractItemModel,但上面的教程引用却暗示了其他情况(其中指出,不必对模型进行任何更改)。

相关问题

QTreeView always displaying the same data

完整的工作示例

from PySide import QtGui, QtCore

class Food(object):
    def __init__(self, name, shortDescription, note, parent = None):
        self.data = (name, shortDescription, note);
        self.parentIndex = parent

class FavoritesTableModel(QtCore.QAbstractTableModel):
    def __init__(self):
        QtCore.QAbstractTableModel.__init__(self)
        self.foods = []  
        self.loadData() 

    def data(self, index, role = QtCore.Qt.DisplayRole):
        if role == QtCore.Qt.DisplayRole:
            return self.foods[index.row()].data[index.column()]
        return None

    def rowCount(self, index=QtCore.QModelIndex()):
        return len(self.foods)

    def columnCount(self, index=QtCore.QModelIndex()):
        return 3

    def index(self, row, column, parent = QtCore.QModelIndex()):  
        return self.createIndex(row, column, parent)

    def loadData(self):   
        allFoods=("Apples", "Pears", "Grapes", "Cookies", "Stinkberries")
        allDescs = ("Red", "Green", "Purple", "Yummy", "Huh?")
        allNotes = ("Bought recently", "Kind of delicious", "Weird wine grapes",
                    "So good...eat with milk", "Don't put in your nose")
        for name, shortDescription, note in zip(allFoods, allDescs, allNotes):
            food = Food(name, shortDescription, note)                                      
            self.foods.append(food) 

def main():
    import sys
    app = QtGui.QApplication(sys.argv)

    model = FavoritesTableModel() 

    #Table view
    view1 = QtGui.QTableView()
    view1.setModel(model)
    view1.show()

    #Tree view
    view2 = QtGui.QTreeView()
    view2.setModel(model)
    view2.show()

    sys.exit(app.exec_())

if __name__ == '__main__':
    main()

【问题讨论】:

    标签: qt pyqt pyside


    【解决方案1】:

    来自official documentation

    在实现基于表的模型时,rowCount() 应该在父级有效时返回 0。

    columnCount() 也是如此。为了完整起见,data() 应该在父级有效时返回 None


    事情是这样的:

    1. 您点击“Stinkberries”旁边的“+”号。
    2. QTreeView 认为,“我需要扩展视图。我想知道 'Stinkberries' 下存在多少行?” 为了找出答案,QTreeView 调用 rowCount(),并传递将“Stinkberries”单元格作为父单元格。
    3. FavoritesTableModel::rowCount() 返回 5,所以 QTreeView 认为,“啊,'Stinkberries' 下有 5 行。”
    4. 列也会发生同样的过程。
    5. QTreeView 决定检索“Stinkberries”下的第一项。它调用 data(),传递第 0 行、第 0 列和“Stinkberries”单元格的索引作为父单元格。
    6. FavoritesTableModel::data() 返回“Apples”,所以 QTreeView 认为,“啊,'Stinkberries' 下的第一项是 'Apples'。”

    要获得正确的行为,您的代码必须为步骤 #3 和 #4 返回 0。


    最后,为了确保“+”号根本不出现,让 hasChildren() 为每个单元格返回 false。

    【讨论】:

    • 感谢您的提示。我不确定如何为 rowCount 获得正确的行为,我会考虑一下。另外,您的意思是如果父级是invalid,它应该返回QVarient() 吗?注意 PySide(我的例子是用它写的)不使用QVariant,所以我稍微编辑了你的答案以包含这种情况。一些代码 sn-ps 会很有帮助,仍然掌握将概念转化为代码的窍门。例如,我尝试(在 rowCount 中)添加:if (index.parent() == None): return 0 但我仍然得到相同的行为。
    • 我添加了def hasChildren(self, index): return False,但我仍然收到+号。
    • 抱歉,我在想 C++。我没有 PySide(甚至 Python)经验,但希望 PySide 行为与原始 C++ 行为相匹配。
    • 我的意思是如果父级是有效,则返回 0(或 None)。原因是这样的:所有顶级项都没有父项;只有嵌套的树项具有有效的父项。表模型不包含嵌套项,因此模型中的所有项都应该没有父项。 (请澄清:我的编号列表的哪些部分你明白,哪些部分你不明白?)
    • 我注意到你声明了def rowCount(self, index=QtCore.QModelIndex())。为什么将参数命名为“索引”?该参数实际上是指父级。 (见pyside.github.io/docs/pyside/PySide/QtCore/…)无论如何,正确的代码应该是def rowCount(self, parent=QtCore.QModelIndex()): if (parent != None) return 0 ...
    【解决方案2】:

    解决方案

    理论上,模型视图框架的一个很好的特性是您可以拥有同一个模型的多个视图。但实际上,QAbstractTableModel 的真正目的是帮助您查看表格,而不是树。 The documentation for QAbstractTableModel 说:

    由于模型提供了比 QAbstractItemModel,它不适合与树视图一起使用

    但是,即使有这个警告,也有办法让它发挥作用。首先,正如 JKSH 所指出的,您必须修复 rowCount(并注意它的第二个参数是 parent 索引):

    def rowCount(self, parent=QtCore.QModelIndex()):
        if parent.isValid():
            return 0
        return len(self.foods)
    

    其次,删除 index 的不切实际的重新实现,这使得选择行为变得非常奇怪,原因我坦率地不明白。

    不过,一般来说,如果您想要一个通用模型,那么可以放心使用它并从QAbstractItemModel 继承,而不是预制模型之一。

    讨论

    在表模型中忽略 rowCount 中的父级是可以接受的。在官方的 Qt 书中,他们遵循让rowCount 仅返回要在表中显示的行数的标准过程。布兰切特和萨默菲尔德注:

    父参数对表模型没有意义;它在那里 因为 rowCount() 和 columnCount() 是继承自 more 支持层次结构的通用 QAbstractItemModel 基类。 (p 第255章)

    Summerfield 在他的 PyQt 书中指出:

    [T]父 QModelIndex p 仅与树模型有关 (p 434)

    基本上,rowCount 告诉您在父项下方显示多少行。因为在表中所有项目都具有相同的父项,所以 QTableViews 中不使用父项。但是由于 JKSH 在他的回答中很好地指出了原因,这种策略不适用于树。

    因此,应修改父参数“对表模型没有意义”的声明,条件是只有当数据将由QTableView 专门显示时,这才是正确的 (这通常是一个很好的假设)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-03
      • 1970-01-01
      • 2021-10-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多