Qt:QAbstractItemModel中的setData方法

8

我是model view的新手,一边跟着这个教程,一边查看文档,然后我发现了一个小细节:在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

我发现即使函数返回False,如果颜色有效,视图仍然会得到更新。我是否误解了setData方法中的返回角色,或者这是一个bug?
供参考,我使用的是PySide 1.2.1,而不是PyQt4。
3个回答

5
引用视频教程中关于setData的内容:

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

严格来说,这种说法是不正确的。 QAbstractItemModel的文档只说明setData在数据设置成功时返回true,否则返回false;它没有提到可能产生的后果。具体而言,它没有提到任何关于更新视图的内容。
从Qt源代码可以看出,setData的返回值在一些地方得到检查,其中一些检查有时候可以帮助触发更新。但是有数十个因素可以触发更新,因此setData的返回值绝不是更新项所必需的。
也许更准确的说法是setData应该返回true,否则在某些情况下视图可能不会更新。

@LaszloPapp。我认为,您的回答和评论很好地解决了如何正确实现“setdata”的问题,但并没有直接回应视频教程中提出的声明。可能OP应该更清楚地表明他真正想问什么。无论如何,我给了您一个赞,因为您的回答和评论似乎涵盖了原始问题中的所有要点。 - ekhumoro
@ekhumoro:我的评论和答案都涉及到了教程代码。请再次阅读:“此外,您似乎参考的教程在您的代码中使用了self.__colors集合来进行rowCount()、data()和其他方法。如果您想避免更新,您需要在任何这样的语句之前返回False。”此外,您的答案(至少对我来说)似乎会“随机”更新,这是不正确的,并且没有像我的答案那样提供实际细节。无论如何,我认为OP只是因为他不得不理解这么多东西而感到沮丧。 - László Papp
无论如何,我很高兴能够忘记这个帖子,通过了解原帖作者现在感到满意,并得到他想要听到的答案来继续前进。 - László Papp
@LaszloPapp。我认为我们并没有真正的分歧。你从Python的角度看待它,而我则从Qt的角度看待它。在我们之间,OP似乎已经得到了他想要的东西,所以我们的工作完成了。 - ekhumoro
@ekhumoro: 我从两个方面考虑了。我甚至贴出了相关的Qt文档,并解释了Qt API为什么是这样的。无论如何,我同意工作已经完成了,所以让我们继续吧。 :-) - László Papp
显示剩余5条评论

2
我是否误解了setData方法中的返回角色,还是这是一个错误? 从Qt documentation中可以看到:
bool QAbstractItemModel::setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole) [virtual] 将索引处项目的角色数据设置为值。 如果成功,则返回true;否则返回false。 如果成功设置数据,则应发出dataChanged()信号。 基类实现返回false。必须重新实现此函数和data()以用于可编辑模型。
然而,根据返回值,即使数据没有成功设置,您似乎仍会发出dataChanged()信号。 此外,您似乎参考的教程在您的代码中使用self.__colors集来进行rowCount(),data()和其他方法。 如果您想避免更新,则需要在任何此类语句之前返回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呢? - Ilyes Ferchiou
当删除 dataChanged 时唯一改变的是只有我进行修改的视图得到了更新,使用相同模型的其他视图直到我用鼠标将焦点设置在它们上面才会更新。 - Ilyes Ferchiou
如果您查看整个代码,您会发现它基于同一模型有4个视图。正如我已经说过的,如果我在view1中双击一个项目并进行修改,即使我没有发出信号,它也会在该视图(view1)中更新!但是其他3个视图直到我用鼠标设置焦点才会更新。因此,信号只是提醒其他视图模型已更改,但不会更改修改发生的视图的情况。 - Ilyes Ferchiou
@IlyesFerchiou:我想我会继续前进。 :) 我已经写了你需要理解的所有内容。 - László Papp
@IlyesFerchiou:我不确定教程代码中有什么不清楚的地方。它确实在rowCount()、data()等函数中使用了self.__colors。显然,如果你改变了它,你将会有更多的行、数据等,所以当你想要更新时,你需要使用它,并且在任何这样的语句之前必须返回False。 - László Papp
显示剩余8条评论

1
我无法找到太多关于此事的信息。然而,一位名为“Qt专家”的论坛帖子表明,这种行为是Qt开发人员的设计选择:

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

更具体地说,视图不会因模型拒绝输入而丢失您的输入。在您所遵循的教程的背景下(其中颜色与模型不同步),这可能看起来很奇怪,但在某些情况下,这可能是可取的。
例如,假设您使用和设计了一个表单,将表单的内容映射到模型中。让我们还假设设置为。在模式下,每次编辑并失去焦点时,都会更新模型。如果模型也拒绝更改,并且当前数据(sans更改)重新填充到中,则用户必须重新开始(而不是修复其条目)。
另一种设计选择是允许视图与模型不同步,如果这是不希望的,则将更改此行为的责任推给程序员。
我可以想到两种手头改变此行为的方法:
  1. 创建一个自定义代理来处理setData -> false,通过发出自定义的dataRejected信号,供视图连接并用于更新自身。
  2. 为您的模型创建一个“无状态”视图:强制视图在更新自身时从模型中检索数据。通过这种方式,向模型提交潜在更改将不会更新视图,除非该模型发出dataChanged信号,在这种情况下,视图将检索当前状态并更新自身。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接