QStandardItemModel 的 setData、setItemData 和 setIem 方法有什么区别?
What's difference setData, setItemData and setIem method of QStandardItemModel?
我很困惑如何使用QStandardItemModel
的setData, 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 模型的概念,您应该阅读以下指南:
- Model/View Programming
- Model/View Tutorial
- Why Qt is misusing model/view terminology?
- 每个方法的文档:setData(), setItemData() and setItem()。
以前的概念:
- 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"),
},
)
setData
和setItemData
都非常相似。
你要明白的是,Qt模型使用角色将某些数据分配给每个"index"。这意味着每个索引(对模型行和列的引用,如果模型支持树,则可能包括父项)可以附加不同的数据。最常用的数据角色是“DisplayRole
”,这是项目视图通常显示为文本的内容;但通常会实现其他数据(请参阅 ItemDataRole
,这有助于项目视图向用户正确显示模型数据。
setData
和 setItemData
之间最重要的区别是映射。您正在做的事情不起作用,因为您使用的关键字未被识别为 可用 角色。
在您的示例 ({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_())
我很困惑如何使用QStandardItemModel
的setData, 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 模型的概念,您应该阅读以下指南:
- Model/View Programming
- Model/View Tutorial
- Why Qt is misusing model/view terminology?
- 每个方法的文档:setData(), setItemData() and setItem()。
以前的概念:
- 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"),
},
)
setData
和setItemData
都非常相似。
你要明白的是,Qt模型使用角色将某些数据分配给每个"index"。这意味着每个索引(对模型行和列的引用,如果模型支持树,则可能包括父项)可以附加不同的数据。最常用的数据角色是“DisplayRole
”,这是项目视图通常显示为文本的内容;但通常会实现其他数据(请参阅 ItemDataRole
,这有助于项目视图向用户正确显示模型数据。
setData
和 setItemData
之间最重要的区别是映射。您正在做的事情不起作用,因为您使用的关键字未被识别为 可用 角色。
在您的示例 ({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_())