【问题标题】:What's difference setData, setItemData and setIem method of QStandardItemModel?QStandardItemModel 的 setData、setItemData 和 setIem 方法有什么区别?
【发布时间】:2020-03-06 18:23:04
【问题描述】:

我很困惑如何使用QStandardItemModelsetDatasetItemDatasetItem方法,这些方法似乎有相同的效果,不知道我应该选择哪种方法最好?

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()

【问题讨论】:

  • model.setItemData(index, {1: 'a', 2: 'b'}) 语句似乎不起作用?
  • 很少看到setData 被这样使用,而setItemData 几乎从未被使用过。更常见的是使用QStandardItem 方法,如setTextsetIconsetForeground 等。如果需要添加自定义数据,通常使用带有用户定义角色的item.setData() 来完成。 setItem 方法最常用于填充表(对于其他视图,appendRow 更常见),或替换现有项目时。它永远不会在setData 通常出现的地方使用。 (PS:你也应该避免在 PyQt5 中使用QVariant,除非你知道你在做什么)。
  • @ekhumoro 感谢您的建议,我现在正在学习 pyqt 并不太清楚某些 API 之间的区别。

标签: pyqt pyqt5 qabstractitemmodel


【解决方案1】:

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

以前的概念:

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

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

  • setData(): 每个 Qt 模型都继承自 QAbstractItemModel,因此此类定义了一般行为,在这种情况下,定义 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的信息,并且通过简单的方式您可以说它是模型的一个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"),
        },
    )

【讨论】:

  • 很好的例子来展示它们的用法。我会花一些时间阅读指南,这真的是一篇很长的文章。
【解决方案2】:

setDatasetItemData 非常相似。

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

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

最后,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_())

【讨论】:

  • 很常见的解释,我明白了,现在该怎么做。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-26
  • 2018-05-14
  • 2010-09-14
  • 2016-05-03
  • 2020-03-11
相关资源
最近更新 更多