I have a test code for QListView
& QIdentityProxyModel
.
When I implement QIdentityProxyModel
, the error occured at the setData
, func of dataChanged
.If I don't implement it, the error didn't occur.
I tried to do the same thing by PyQt6
, but the result was the same.
I tried to do by command prompt, no error massage.
On the contrary, I can success implementing QIdentityProxyModel
on the case of QTreeView
, QTableView
with no problem.I have been thinking QListView
is the easiest in these viewer.
Where is the problem in my code?
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()
The
insertRows()
function should not usebeginResetModel()
(which, as the name suggests, begins a model reset), norlayoutChanged()
: not only it should always be called following alayoutAboutToBeChanged()
, but that feature should only be used when the model is being sorted.Emitting
dataChanged()
is also pointless, as it's implied that an inserted row will cause the view to calldata()
on the new indexes.Also note that you defined
index()
twice, but only the second one is used and actually correct, due to theif row > len(self.items) - 1:
check opposed to>=
of the previous, which is wrong. In any case, if you're using QAbstractListItem, which is a mono dimensional model, there's absolutely no need to overrideindex()
and you can use the default implementation.