QListView & QIdentityProxyModel
QListView & QIdentityProxyModel
我有 QListView
& QIdentityProxyModel
的测试代码。
当我实现QIdentityProxyModel
时,错误发生在setData
,函数dataChanged
。如果我不实现它,则不会发生错误。
我尝试用PyQt6
做同样的事情,但结果是一样的。
我试着通过命令提示符来做,没有错误消息。
相反,我可以在QTreeView
的情况下成功实施QIdentityProxyModel
,QTableView
没有problem.I一直认为QListView
是在这些查看器中最简单。
我的代码哪里出了问题?
from PySide6 import QtWidgets, QtGui, QtCore
import sys
class ListView(QtWidgets.QListView):
def __init__(self, parent=None):
super(ListView, self).__init__(parent)
def insertItem(self):
self.model().insertRows(0, 1)
self.model().setData(self.model().index(0, 0), ListItem(text="Hallo!"))
class ListItem(object):
def __init__(self, text="", parent=None):
super(ListItem, self).__init__()
self.data = []
self.text = "Hi!" if not text else text
class ListModel(QtCore.QAbstractListModel):
def __init__(self, parent=None):
super(ListModel, self).__init__(parent)
self.items = []
def columnCount(self, parent=QtCore.QModelIndex()):
return 1
def rowCount(self, parent=QtCore.QModelIndex()):
return len(self.items)
def headerData(self, index):
return "LIST"
def index(self, row, column, _parent=QtCore.QModelIndex()):
if row >= len(self.items) - 1:
return QtCore.QModelIndex()
if self.items:
childIndex = self.createIndex(row, column, self.items[row])
return childIndex
return QtCore.QModelIndex()
def data(self, index, role=QtCore.Qt.ItemDataRole.DisplayRole):
if not index.isValid():
return None
row = index.row()
column = index.column()
if role == QtCore.Qt.DisplayRole:
if 0 <= row < self.rowCount() and 0 <= column < self.columnCount():
return self.items[row].text
def setData(self, index, value, role=QtCore.Qt.ItemDataRole.EditRole):
if role == QtCore.Qt.ItemDataRole.EditRole:
row = index.row()
if 0 <= row < self.rowCount():
self.items[row] = value
self.dataChanged["QModelIndex", "QModelIndex"].emit(index, index)
return True
return False
def flags(self, index):
return QtCore.Qt.ItemFlag.ItemIsEnabled|QtCore.Qt.ItemFlag.ItemIsSelectable
def index(self, row, column, parent=QtCore.QModelIndex()):
if row > len(self.items) - 1:
return QtCore.QModelIndex()
elif self.items:
childIndex = self.createIndex(row, column, self.items[row])
return childIndex
return QtCore.QModelIndex()
def insertRows(self, position, rows=1, parent=QtCore.QModelIndex()):
self.beginResetModel()
self.beginInsertRows(parent, position, position+rows-1)
for i in range(rows):
self.items.insert(position+i, ListItem())
self.endInsertRows()
self.dataChanged["QModelIndex", "QModelIndex"].emit(parent, parent)
self.layoutChanged.emit()
self.endResetModel()
return True
class ListIdentityModel(QtCore.QIdentityProxyModel):
def __init__(self, parent=None):
super(ListIdentityModel, self).__init__(parent)
def mapFromSource(self, sourceIndex):
if not sourceIndex.isValid():
return QtCore.QModelIndex()
return self.createIndex(sourceIndex.row(), sourceIndex.column(), sourceIndex.internalPointer())
def mapSelectionFromSource(self, selection):
sourceIndexes = selection.indexes()
proxySelection = QtCore.QItemSelection(sourceIndexes[0], sourceIndexes[-1])
return proxySelection
def mapSelectionToSource(self, selection):
proxyIndexes = selection.indexes()
sourceSelection = QtCore.QItemSelection(proxyIndexes[0], proxyIndexes[-1])
return sourceSelection
def mapToSource(self, proxyIndex):
if not proxyIndex.isValid():
return QtCore.QModelIndex()
return self.sourceModel().createIndex(proxyIndex.row(), proxyIndex.column(), proxyIndex.internalPointer())
def setSourceModel(self, sourceModel):
self.beginResetModel()
super(ListIdentityModel, self).setSourceModel(sourceModel)
self.endResetModel()
def main():
app = QtWidgets.QApplication([]) if QtWidgets.QApplication.instance() is None else QtWidgets.QApplication.instance()
widget = QtWidgets.QWidget()
pushbutton = QtWidgets.QPushButton("insert item")
main_view = ListView()
model = ListModel(main_view)
main_view.setModel(model)
#comment or uncomment
identity_view = ListView()
identityModel = ListIdentityModel()
identity_view.setModel(identityModel)
identityModel.setSourceModel(model)
#
hboxlayout = QtWidgets.QHBoxLayout()
hboxlayout.addWidget(pushbutton)
hboxlayout.addWidget(main_view)
#
hboxlayout.addWidget(identity_view)
#
widget.setLayout(hboxlayout)
pushbutton.clicked.connect(main_view.insertItem)
widget.show()
sys.exit(app.exec())
if __name__ == "__main__":
main()
insertRows()
函数不应使用 beginResetModel()
(顾名思义,它会开始一个模型 reset),也不应使用 layoutChanged()
: 不仅应该始终在 layoutAboutToBeChanged()
之后调用它,而且该功能只应在模型 排序 .
时使用
发出 dataChanged()
也是毫无意义的,因为它暗示插入的行将导致视图在新索引上调用 data()
。
def insertRows(self, position, rows=1, parent=QtCore.QModelIndex()):
self.beginInsertRows(parent, position, position+rows-1)
for i in range(rows):
self.items.insert(position+i, ListItem())
self.endInsertRows()
return True
另请注意,您定义了两次 index()
,但仅使用了第二个并且实际上是正确的,因为 if row > len(self.items) - 1:
检查与前一个 >=
相反,这是错误的。在任何情况下,如果您使用的是单维模型 QAbstractListItem,则绝对没有必要重写 index()
,您可以使用默认实现。
我有 QListView
& QIdentityProxyModel
的测试代码。
当我实现QIdentityProxyModel
时,错误发生在setData
,函数dataChanged
。如果我不实现它,则不会发生错误。
我尝试用PyQt6
做同样的事情,但结果是一样的。
我试着通过命令提示符来做,没有错误消息。
相反,我可以在QTreeView
的情况下成功实施QIdentityProxyModel
,QTableView
没有problem.I一直认为QListView
是在这些查看器中最简单。
我的代码哪里出了问题?
from PySide6 import QtWidgets, QtGui, QtCore
import sys
class ListView(QtWidgets.QListView):
def __init__(self, parent=None):
super(ListView, self).__init__(parent)
def insertItem(self):
self.model().insertRows(0, 1)
self.model().setData(self.model().index(0, 0), ListItem(text="Hallo!"))
class ListItem(object):
def __init__(self, text="", parent=None):
super(ListItem, self).__init__()
self.data = []
self.text = "Hi!" if not text else text
class ListModel(QtCore.QAbstractListModel):
def __init__(self, parent=None):
super(ListModel, self).__init__(parent)
self.items = []
def columnCount(self, parent=QtCore.QModelIndex()):
return 1
def rowCount(self, parent=QtCore.QModelIndex()):
return len(self.items)
def headerData(self, index):
return "LIST"
def index(self, row, column, _parent=QtCore.QModelIndex()):
if row >= len(self.items) - 1:
return QtCore.QModelIndex()
if self.items:
childIndex = self.createIndex(row, column, self.items[row])
return childIndex
return QtCore.QModelIndex()
def data(self, index, role=QtCore.Qt.ItemDataRole.DisplayRole):
if not index.isValid():
return None
row = index.row()
column = index.column()
if role == QtCore.Qt.DisplayRole:
if 0 <= row < self.rowCount() and 0 <= column < self.columnCount():
return self.items[row].text
def setData(self, index, value, role=QtCore.Qt.ItemDataRole.EditRole):
if role == QtCore.Qt.ItemDataRole.EditRole:
row = index.row()
if 0 <= row < self.rowCount():
self.items[row] = value
self.dataChanged["QModelIndex", "QModelIndex"].emit(index, index)
return True
return False
def flags(self, index):
return QtCore.Qt.ItemFlag.ItemIsEnabled|QtCore.Qt.ItemFlag.ItemIsSelectable
def index(self, row, column, parent=QtCore.QModelIndex()):
if row > len(self.items) - 1:
return QtCore.QModelIndex()
elif self.items:
childIndex = self.createIndex(row, column, self.items[row])
return childIndex
return QtCore.QModelIndex()
def insertRows(self, position, rows=1, parent=QtCore.QModelIndex()):
self.beginResetModel()
self.beginInsertRows(parent, position, position+rows-1)
for i in range(rows):
self.items.insert(position+i, ListItem())
self.endInsertRows()
self.dataChanged["QModelIndex", "QModelIndex"].emit(parent, parent)
self.layoutChanged.emit()
self.endResetModel()
return True
class ListIdentityModel(QtCore.QIdentityProxyModel):
def __init__(self, parent=None):
super(ListIdentityModel, self).__init__(parent)
def mapFromSource(self, sourceIndex):
if not sourceIndex.isValid():
return QtCore.QModelIndex()
return self.createIndex(sourceIndex.row(), sourceIndex.column(), sourceIndex.internalPointer())
def mapSelectionFromSource(self, selection):
sourceIndexes = selection.indexes()
proxySelection = QtCore.QItemSelection(sourceIndexes[0], sourceIndexes[-1])
return proxySelection
def mapSelectionToSource(self, selection):
proxyIndexes = selection.indexes()
sourceSelection = QtCore.QItemSelection(proxyIndexes[0], proxyIndexes[-1])
return sourceSelection
def mapToSource(self, proxyIndex):
if not proxyIndex.isValid():
return QtCore.QModelIndex()
return self.sourceModel().createIndex(proxyIndex.row(), proxyIndex.column(), proxyIndex.internalPointer())
def setSourceModel(self, sourceModel):
self.beginResetModel()
super(ListIdentityModel, self).setSourceModel(sourceModel)
self.endResetModel()
def main():
app = QtWidgets.QApplication([]) if QtWidgets.QApplication.instance() is None else QtWidgets.QApplication.instance()
widget = QtWidgets.QWidget()
pushbutton = QtWidgets.QPushButton("insert item")
main_view = ListView()
model = ListModel(main_view)
main_view.setModel(model)
#comment or uncomment
identity_view = ListView()
identityModel = ListIdentityModel()
identity_view.setModel(identityModel)
identityModel.setSourceModel(model)
#
hboxlayout = QtWidgets.QHBoxLayout()
hboxlayout.addWidget(pushbutton)
hboxlayout.addWidget(main_view)
#
hboxlayout.addWidget(identity_view)
#
widget.setLayout(hboxlayout)
pushbutton.clicked.connect(main_view.insertItem)
widget.show()
sys.exit(app.exec())
if __name__ == "__main__":
main()
insertRows()
函数不应使用 beginResetModel()
(顾名思义,它会开始一个模型 reset),也不应使用 layoutChanged()
: 不仅应该始终在 layoutAboutToBeChanged()
之后调用它,而且该功能只应在模型 排序 .
发出 dataChanged()
也是毫无意义的,因为它暗示插入的行将导致视图在新索引上调用 data()
。
def insertRows(self, position, rows=1, parent=QtCore.QModelIndex()):
self.beginInsertRows(parent, position, position+rows-1)
for i in range(rows):
self.items.insert(position+i, ListItem())
self.endInsertRows()
return True
另请注意,您定义了两次 index()
,但仅使用了第二个并且实际上是正确的,因为 if row > len(self.items) - 1:
检查与前一个 >=
相反,这是错误的。在任何情况下,如果您使用的是单维模型 QAbstractListItem,则绝对没有必要重写 index()
,您可以使用默认实现。