Qt:QAbstractItemModel 中的 setData 方法

Posted

技术标签:

【中文标题】Qt:QAbstractItemModel 中的 setData 方法【英文标题】:Qt : setData method in a QAbstractItemModel 【发布时间】:2013-12-26 12:04:17 【问题描述】:

我是模型视图的新手,我一直在关注this tutorial,同时检查文档,我偶然发现了这个小细节:可以下载的教程代码here 在 QAbstractItemModel类(这里是QAbstractListModel)setData方法的代码是:

def setData(self, index, value, role = QtCore.Qt.EditRole):
    if role == QtCore.Qt.EditRole:

        row = index.row()
        color = QtGui.QColor(value)

        if color.isValid():
            self.__colors[row] = color
            self.dataChanged.emit(index, index)
            return True
    return False

根据教程中的解释以及我从文档中了解到的情况,如果函数返回 True,则视图会更新,如果返回 false,则没有任何反应,但是当我将代码更改为:

def setData(self, index, value, role = QtCore.Qt.EditRole):
    if role == QtCore.Qt.EditRole:

        row = index.row()
        color = QtGui.QColor(value)

        if color.isValid():
            self.__colors[row] = color
            self.dataChanged.emit(index, index)
            return False # This is what I changed in the code
    return False

我意识到即使 color.isValid() 函数返回 False,视图仍然会更新。我误解了 setData 方法中的返回角色还是一个错误?

作为参考,我使用的是 PySide 1.2.1,而不是 PyQt4。

【问题讨论】:

【参考方案1】:

引用关于setData的视频教程:

...如果操作成功,该函数需要返回true, 否则视图不会自行更新。

严格来说,这种说法是错误的。 QAbstractItemModel 的文档只说 setData 如果数据设置成功则返回 true,否则返回 false;它没有提到这可能会产生什么后果。具体来说,它没有提及更新视图的任何内容。

查看 Qt 源代码,setData 的返回值确实在一些地方得到检查,其中一些检查有时有助于触发更新.但是实际上有很多东西可以触发更新,所以setData 的返回值对于更新项目来说绝不是必不可少的。

也许更准确地说setData 应该返回true,否则视图可能不会自我更新(在某些情况下)。

【讨论】:

谢谢,这是我想知道的。所以如果设置不正确,它可能会在某个地方搞砸。 @IlyesFerchiou:这是我写的,但更多细节。 :))) 尽管昨天有很多牵手和昨天的努力,但你现在并不客观。不管怎样,我很高兴你现在有一个解决方案。 @LaszloPapp。在我看来,您的回答和 cmets 很好地解决了如何正确实施 setdata 的问题,但它并没有直接解决视频教程中提出的要求。可能 OP 应该更清楚地说明他真正在问什么。无论如何,我给了你一个赞成票,因为你的答案和 cmets 似乎涵盖了原始问题中的所有要点。 @ekhumoro:它确实解决了我的 cmets 和答案中的教程代码。请再读一遍:“此外,您似乎参考的教程正在使用代码中为 rowCount()、`data() 和其他方法设置的 self.__colors。如果您想避免更新,您需要在任何此类声明之前返回 False。”此外,您的答案(至少对我而言)看起来像是“随机”更新,这是不正确的,并且它没有提供有时与我的答案不同的实际细节。无论如何,我认为 OP 只是因为他必须理解这么多事情而感到沮丧。 无论如何,我很高兴忘记这个话题,并继续前进,因为知道 OP 现在感到很高兴,并且有了他想听到的答案。【参考方案2】:

我误解了 setData 方法中的返回角色还是一个错误?

来自Qtdocumentation:

bool QAbstractItemModel::setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole) [虚拟]

将索引项的角色数据设置为值。

如果成功则返回真;否则返回 false。

如果数据设置成功,应该发出 dataChanged() 信号。

基类实现返回 false。必须为可编辑模型重新实现此函数和 data()。

然而,即使数据没有根据返回值成功设置,您似乎也会发出 dataChanged() 信号。此外,您似乎参考的教程是在您的代码中为rowCount()、`data() 和其他方法使用self.__colors 集。如果您想避免更新,则需要在任何此类声明之前返回 False。

您需要注意这些标准,因为信号和颜色是内部管理的,而调用者使用返回值来查看 setData() 方法是否已成功设置。

基于以上知识,您应该已经编写了此代码,以便您第二次尝试使其按预期工作:

def setData(self, index, value, role = QtCore.Qt.EditRole):
    if role == QtCore.Qt.EditRole:

        row = index.row()
        color = QtGui.QColor(value)

        if color.isValid():
            return False
    return False

【讨论】:

谢谢,但我不明白,如果一切都与 dataChanged 信号有关,为什么需要返回 True 或 False ? @IlyesFerchiou:以便调用者可以检查更新是否成功。他们不处理信号。它们只剩下返回值。 感谢您的回答,但我刚刚删除了 dataChanged 信号线,行为仍然相同,即使返回 False 也会反映修改。我猜我评论太快了。所以我们又回到了起点。 @IlyesFerchiou:我无法重现您的问题。请确保使用我粘贴的代码从头开始干净。 删除dataChanged时唯一改变的是只有我进行修改的视图得到了更新,使用相同模型的其他视图直到我用鼠标将焦点放在它们上才更新。 【参考方案3】:

我找不到太多关于此的信息。也就是说,“Qt 专家”的这个论坛帖子表明这种行为是 Qt 开发人员的设计选择:

http://qt-project.org/forums/viewthread/31462

更具体地说,如果您的输入被模型拒绝,视图不会丢失您的输入。这在您所遵循的教程的上下文中可能看起来很奇怪(颜色变化与模型不同步),但在某些情况下这可能是可取的。

例如,假设您使用QLineEdits 和QDataWidgetMapper 设计了一个表单,以将表单的内容映射到您的模型。我们还假设QDataWidgetMapper::SubmitPolicy 设置为AutoSubmit。在AutoSubmit 模式下,每次QLineEdit 被编辑然后失去焦点时,模型都会更新。如果模型也拒绝更改,并且当前数据(无更改)重新填充到 QLineEdit,这将导致用户必须重新开始(而不是修复他们的条目)。

另一种设计选择是允许视图与模型不同步,如果不希望这样做,则将更改此行为的责任推给程序员

我能想到的两种改变这种行为的方法是:

    创建一个自定义委托来处理setData -> false,方法是发出一个自定义dataRejected 信号,视图可以连接到该信号并用于更新自身。 为您的模型创建一个“无状态”视图:强制视图在模型自身更新时从模型中检索数据。通过这种方式,向模型提交潜在更改不会更新视图,除非该模型发出dataChanged 信号,在这种情况下,视图将检索当前状态并自行更新。

【讨论】:

以上是关于Qt:QAbstractItemModel 中的 setData 方法的主要内容,如果未能解决你的问题,请参考以下文章

Qt入门教程数据模型篇QAbstractItemModel抽象模型基类

Qt入门教程数据模型篇QAbstractItemModel抽象模型基类

以编程方式检查QAbstractItemModel / QTreeView中的项目

Qt入门教程高级控件篇QModelIndex索引

如何在 QAbstractItemModel 中为 QTreeView 创建人工节点

Qt QItemSelection::indexes() 返回错误