QListView & QIdentityProxyModel

QListView & QIdentityProxyModel

我有 QListView & QIdentityProxyModel 的测试代码。

当我实现QIdentityProxyModel时,错误发生在setData,函数dataChanged。如果我不实现它,则不会发生错误。

我尝试用PyQt6做同样的事情,但结果是一样的。

我试着通过命令提示符来做,没有错误消息。

相反,我可以在QTreeView的情况下成功实施QIdentityProxyModelQTableView没有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(),您可以使用默认实现。