对模型排序后更新 QListView 选择

Updating QListView selection after sorting the model

对模型进行排序并调用 layoutChanged.emit() 后,我的 QListView 项目显示顺序得到更新,但选择保持在同一行(排序后现在包含另一个项目)。

如何更新选择以跟随排序?

class Window(QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setupUi(self)
        self.sequenceModel = SequenceModel()

    def editSequence(self):
        indexes = self.listView_sch_sequences.selectedIndexes()
        if indexes:
            # Indexes is a list of a single item in single-select mode.
            index = indexes[0]
            sequence = self.sequenceModel.sequences[index.row()]
            sequence.name = self.lineEdit_sch_seq_name.text()
            sequence.speed = self.spinBox_sch_speed.value()
            sequence.order = self.spinBox_order.value()
            self.sequenceModel.sequences.sort(key=lambda x: x.order, reverse=True)
            self.sequenceModel.layoutChanged.emit()

class SequenceModel(QtCore.QAbstractListModel):
    def __init__(self, *args, todos=None, **kwargs):
        super(SequenceModel, self).__init__(*args, **kwargs)
        self.NameRole = Qt.UserRole + 1
        self.DataRole = Qt.UserRole + 2
    
        self.sequences = []

    def data(self, index, role):
        row = index.row()
        if role == Qt.DisplayRole:
            text = self.sequences[row].name
            return text
        if role == self.DataRole:
            sequence = self.sequences[row]
            return sequence

    def rowCount(self, index):
        return len(self.sequences)

class Sequence:
    def __init__(self, name: str, speed: int):
        self.name = name
        self.speed = speed
        self.order = 0

仅更改内部数据结构是不够的:Qt 模型索引(和选择)使用基于行、列和父项的引用来访问内部数据。

如果索引引用第 0 行并且您的数据结构没有跟上索引引用,则该索引将始终引用数据的第一个索引。

选择索引遵循相同的模式,这意味着 模型 更新持久索引引用很重要。每当您更改内部布局时,必须保持对该布局的索引引用,因此如果您需要对当前模型进行排序,则必须调用 changePersistentIndexList().

最好从模型 class 中执行所有这些操作,而不是从外部执行。

这是一个可能的实现:

class SequenceModel(QtCore.QAbstractListModel):
    # ...
    def sortByOrder(self):
        oldIndexes = [self.index(i, 0) for i in range(len(self.sequences))]
        newIndexes = sorted(
            oldIndexes, 
            key=lambda i: i.data(self.DataRole).order, 
            reverse=True
        )
        self.changePersistentIndexList(oldIndexes, newIndexes)
        self.layoutChanged.emit()

然后,在设置新的序列实例属性后立即从 editSequence 调用 self.sequenceModel.sortByOrder()