对模型排序后更新 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()
。
对模型进行排序并调用 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()
。