QStandardItemModel 的 setData、setItemData 和 setIem 方法有什么区别?

What's difference setData, setItemData and setIem method of QStandardItemModel?

我很困惑如何使用QStandardItemModelsetData, setItemData and setItem方法,这几个方法好像是一样的效果,我想知道我应该选择哪种方法最好?

class DemoD(QMainWindow):
    def __init__(self):
        super().__init__()
        self.init_ui()

    def init_ui(self):
        table = QTableView()
        model = QStandardItemModel(4, 2)

        table.setModel(model)

        # delegate = SpinBoxDelegate()

        # table.setItemDelegate(delegate)

        for row in range(4):
            for col in range(2):
                item = QStandardItem('china')
                model.setItem(row, col, item)

                index = model.index(row, col)
                value = QVariant((row + 1)*(col + 1))
                model.setData(index, value)

                model.setItemData(index, {1: 'a', 2: 'b'})

        self.setCentralWidget(table)

        self.resize(400, 300)

app = QApplication([])
demo = DemoD()
demo.show()
app.exec()

如果您想了解 Qt 模型的概念,您应该阅读以下指南:

以前的概念:

  • QStandarItemModel:它是一个class继承自QAbstractItemModel,允许存储任何类型的信息,不像QAbstractItemModel只定义行为。

考虑到您已经仔细阅读了前面的链接,我们将尝试解释您指出的不同方法之间的区别:

  • setData(): 每个 Qt 模型都继承自 QAbstractItemModel 所以这个 class 定义了一般行为,在这种情况下定义了 setData() 模型负责修改与 QModelIndex 关联的角色信息。换句话说,如果你想实现一个可编辑的模型,它是你必须实现的通用方法,例如 QStringListModel 不是一个可编辑的模型所以它不实现它但是在 QStandardItemModel 的情况下它是可编辑的所以你可以修改通过该方法的模型信息。

  • setItem(): QStandardItem是QStandardItemModel的一个概念,在概念上类似于QModelIndex。该元素允许您轻松地与 QModelIndex 进行交互。如果 QStandardItem 没有与模型相关联,它只会存储信息,在分配模型时,所有信息都会传递给模型,并且模型会通知您可以通过其他方法(例如 setData)进行的任何更改。与模型的 setData 等效的是 QStandardItem 的 setData 方法,但后者不需要提供 QModelIndex,因为该信息在内部可用或可以在建立模型时获得。

例如:

it.setText("foo")
it.setTextAlignment(QtCore.Qt.AlignCenter)

等同于

it.model().setData(it.index(), "foo", QtCore.Qt.DisplayRole)
it.model().setData(it.index(), QtCore.Qt.AlignCenter, QtCore.Qt.TextAlignmentRole)

如你所见,QStandardItem允许你通过简单的方式修改item的信息,简单的说它是model的item。

  • setItemData(): 该方法允许您通过检查角色是否有效来修改与 QModelIndex 关联的多个角色的信息,通常如果您使用无效模型,该方法将不会更新信息,但在处理通用信息的 QStandardItemModel 的情况下,确定所有角色对于将始终有效的内容有效。

在 QStandardItemModel 的情况下,以下代码是等效的:

import sys
from PyQt5 import QtCore, QtGui, QtWidgets

if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    model = QtGui.QStandardItemModel(1, 1)
    it = QtGui.QStandardItem()
    model.setItem(0, 0, it)

    # The following lines modify the text shown
    # to be related to the Qt::DisplayRole role and
    # the QModelIndex associated with the QStandardItem:
    it.setText("foo")
    it.setData("foo", QtCore.Qt.DisplayRole)
    model.setData(it.index(), "foo", QtCore.Qt.DisplayRole)
    model.setItemData(it.index(), {QtCore.Qt.DisplayRole: "foo"})

    # The same as the previous lines but in the case of
    # the background and the text colors of the item.
    it.setForeground(QtGui.QColor("red"))
    it.setBackground(QtGui.QColor("blue"))
    it.setData(QtGui.QColor("red"), QtCore.Qt.ForegroundRole)
    it.setData(QtGui.QColor("blue"), QtCore.Qt.BackgroundRole)
    model.setData(it.index(), QtGui.QColor("red"), QtCore.Qt.ForegroundRole)
    model.setData(it.index(), QtGui.QColor("blue"), QtCore.Qt.BackgroundRole)
    model.setItemData(
        it.index(),
        {
            QtCore.Qt.ForegroundRole: QtGui.QColor("red"),
            QtCore.Qt.BackgroundRole: QtGui.QColor("blue"),
        },
    )

setDatasetItemData都非常相似。

你要明白的是,Qt模型使用角色将某些数据分配给每个"index"。这意味着每个索引(对模型行和列的引用,如果模型支持树,则可能包括父项)可以附加不同的数据。最常用的数据角色是“DisplayRole”,这是项目视图通常显示为文本的内容;但通常会实现其他数据(请参阅 ItemDataRole,这有助于项目视图向用户正确显示模型数据。

setDatasetItemData 之间最重要的区别是映射。您正在做的事情不起作用,因为您使用的关键字未被识别为 可用 角色。
在您的示例 ({1: 'a', 2: 'b'}) 中,1 映射到 DecorationRole(在项目视图中用于显示装饰 - 一个图标)并且 2 映射到 EditRole,每当用户想要编辑该项目的内容时使用,可以与显示的内容不同(考虑以短格式输入日期,例如“10/11 ",它可以是最终显示为 "november 10 2019") 的实际日期。

最后,setItem 是 QStandardItemModel 的一个特殊函数,它使用新提供的 QStandardItem 对象创建一个新项目(或覆盖现有项目)。

我提供了一个测试示例,可以更好地展示在所有三种情况下发生的情况。

from PyQt5 import QtCore, QtGui, QtWidgets

class Window(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        layout = QtWidgets.QVBoxLayout(self)
        self.table = QtWidgets.QTableView()
        layout.addWidget(self.table)

        # hide headers, we're not interested
        self.table.horizontalHeader().setVisible(False)
        self.table.verticalHeader().setVisible(False)
        self.table.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch)

        self.model = QtGui.QStandardItemModel()
        self.table.setModel(self.model)
        for item in range(1, 6):
            item = QtGui.QStandardItem('item {}'.format(item))
            self.model.appendRow(item)

        toolLayout = QtWidgets.QHBoxLayout()
        layout.addLayout(toolLayout)

        self.itemTextEdit = QtWidgets.QLineEdit('text')
        toolLayout.addWidget(self.itemTextEdit)
        self.itemSetTextButton = QtWidgets.QPushButton('Set text')
        toolLayout.addWidget(self.itemSetTextButton)
        self.itemSetTextButton.clicked.connect(self.setText)

        toolLayout.addSpacing(5)
        self.itemAlignCombo = QtWidgets.QComboBox()
        toolLayout.addWidget(self.itemAlignCombo)
        for alignText in ('Left', 'Center', 'Right'):
            alignment = QtCore.Qt.AlignVCenter | getattr(QtCore.Qt, 'Align{}'.format(alignText))
            self.itemAlignCombo.addItem(alignText, alignment)
        self.itemSetAlignButton = QtWidgets.QPushButton('Set alignment')
        toolLayout.addWidget(self.itemSetAlignButton)
        self.itemSetAlignButton.clicked.connect(self.setAlignment)

        self.table.setCurrentIndex(self.model.index(0, 0))

        toolLayout.addSpacing(5)
        self.setDataButton = QtWidgets.QPushButton('SetItemData()')
        toolLayout.addWidget(self.setDataButton)
        self.setDataButton.clicked.connect(self.setItemData)

        setItemLayout = QtWidgets.QHBoxLayout()
        layout.addLayout(setItemLayout)
        self.itemRowSpin = QtWidgets.QSpinBox()
        setItemLayout.addWidget(self.itemRowSpin)
        self.itemRowSpin.setRange(1, self.model.rowCount() + 1)
        self.itemRowSpin.setValue(self.itemRowSpin.maximum())
        self.setItemButton = QtWidgets.QPushButton('SetItem()')
        setItemLayout.addWidget(self.setItemButton)
        self.setItemButton.clicked.connect(self.setItem)

    def setText(self):
        # set the text of the current item
        index = self.table.currentIndex()
        self.model.setData(index, self.itemTextEdit.text())

    def setAlignment(self):
        # set the alignment of the current item
        index = self.table.currentIndex()
        self.model.setData(index, self.itemAlignCombo.currentData(), QtCore.Qt.TextAlignmentRole)

    def setItemData(self):
        # set *both* text and alignment of the current item
        index = self.table.currentIndex()
        self.model.setItemData(index, {
            QtCore.Qt.DisplayRole: self.itemTextEdit.text(), 
            QtCore.Qt.TextAlignmentRole: self.itemAlignCombo.currentData()
        })

    def setItem(self):
        # set a new item for the selected row with the selected text and alignment
        item = QtGui.QStandardItem()
        item.setText(self.itemTextEdit.text())
        item.setTextAlignment(QtCore.Qt.Alignment(self.itemAlignCombo.currentData()))
        self.model.setItem(self.itemRowSpin.value() - 1, 0, item)
        self.itemRowSpin.setMaximum(self.model.rowCount() + 1)
        self.itemRowSpin.setValue(self.itemRowSpin.maximum())


if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())