6

I am confused how to use setData, setItemData and setItem method of QStandardItemModel, these method seem have the same effect, i wonder to know which method should i choose to use will be best?

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

jett chen
  • 1,067
  • 16
  • 33
  • `model.setItemData(index, {1: 'a', 2: 'b'})` statement seems not working? – jett chen Nov 11 '19 at 03:00
  • It's quite rare to see `setData` used like that, and `setItemData` is hardly ever used at all. It's much more common that `QStandardItem` methods like `setText`, `setIcon`, `setForeground`, etc are used. If custom data needs to be added, this is normally done using `item.setData()` with a user-defined role. The `setItem` method is most commonly used when populating tables (for other views, `appendRow` is more common), or when replacing existing items. It is never used where `setData` normally would be. (PS: you should also avoid using `QVariant` in PyQt5, unless you know what you're doing). – ekhumoro Nov 11 '19 at 14:53
  • @ekhumoro Thanks your advice, i am now learning pyqt and not very clear the difference between some API. – jett chen Nov 12 '19 at 00:55

2 Answers2

6

If you want to understand the concepts of a Qt model you should read the following guides:

Previous concepts:

  • QStandarItemModel: It is a class that inherits from QAbstractItemModel that allows to store any type of information unlike QAbstractItemModel that only defines the behavior.

Considering that you have read the previous links carefully, we will try to explain the difference between the different methods that you indicate:

  • setData(): Every Qt model inherits from QAbstractItemModel so this class defines the general behavior, in this case it is defined that the setData() model is responsible for modifying the information of a role associated with a QModelIndex. In other words, it is the generic method that you have to implement if you want to implement an editable model, for example QStringListModel is not an editable model so it does not implement it but in the case of QStandardItemModel it is editable so you can modify the information of the model through of that method.

  • setItem(): QStandardItem is a concept of QStandardItemModel that is conceptually similar to QModelIndex. This element allows you to easily interact with the QModelIndex. If a QStandardItem is not associated with a model it will only store the information, at the time a model is assigned all information is passed to the model, and the model informs you of any changes that can be made by other methods such as setData. An equivalent to setData of the model is the setData method of QStandardItem but the latter does not need to provide the QModelIndex since that information is internally available or can be obtained when a model is established.

For example:

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

is equivalent to

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

As you can see, QStandardItem allows you to modify the information of the item in a simple way, and in a simple way you can say that it is an item of the model.

  • setItemData(): It is a method that allows you to modify the information of several roles associated to a QModelIndex by checking if the roles are valid, in general if you use an invalid model the method will not update the information but in the case of QStandardItemModel that handles the generic information it is established that everything Role is valid for what will always work.

In the case of QStandardItemModel the following codes are equivalent:

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"),
        },
    )
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • Good example to show usage among them. It will spend me some times to read guides, it's realy a very long article. – jett chen Nov 11 '19 at 07:32
4

Both setData and setItemData are very similar.

What you have to understand is that Qt models use roles to assign certain data to each "index". This means that each index (a reference to a model row and column, possibly including a parent if the model supports trees) can have different data attached to it. The most commonly used data role is the "DisplayRole", which is what an item view usually shows as text; but other data is usually implemented (see ItemDataRole, which helps an item view to correctly show the model data to the user.

The most important difference between setData and setItemData is the mapping. What you're doing does not work as the keywords you're using are not recognized as usable roles.
In your example ({1: 'a', 2: 'b'}), 1 maps to DecorationRole (which is used from item views to show a decoration - an icon) and 2 maps to EditRole, which is used whenever the user wants to edit the contents of that item, something that can differ from what's displayed (think about entering a date in a short form such as "10/11", that can be an actual date that is finally shown as "november 10 2019").

Finally, setItem is a special function of QStandardItemModel that creates a new item (or overwrites an existing one) with the new provided QStandardItem object.

I'm providing a test example that will better show what happens in all three situations.

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_())
musicamante
  • 41,230
  • 6
  • 33
  • 58