从 QAbstractListModel 获取选定项

Get Selected Item from QAbstractListModel

如何从包含自定义排序算法的自定义 QAbstractListModel 中检索正确选择的项目?

您可以通过简单地在 UI 中进行选择并查看控制台来测试该工具。您可以看到它为所选项目打印了错误的信息。

我假设问题与我如何使用选择索引获取模型中的项目有关。

完整代码:

import os, sys
from PySide import QtGui, QtCore


class ExplorerItem(object):
    def __init__(self, name, tags):
        self.name = name
        self.tags = tags


class ElementModel(QtCore.QAbstractListModel):
    TagsRole = QtCore.Qt.UserRole + 1
    NameRole = QtCore.Qt.UserRole + 2

    def __init__(self, *args, **kwargs):
        QtCore.QAbstractListModel.__init__(self, *args, **kwargs)
        self._items = []
        self._icons = {}

    def rowCount(self, index=QtCore.QModelIndex()):
        return len(self._items)

    def addItem(self, item):
        self.beginInsertRows(QtCore.QModelIndex(), self.rowCount(), self.rowCount())
        self._items.append(item)
        self.endInsertRows()

    def getItem(self, index):
        row = index.row()
        if index.isValid() and 0 <= row < self.rowCount():
            return self._items[row]

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if not index.isValid():
            return None
        if 0 <= index.row() < self.rowCount():
            item = self._items[index.row()]
            if role == ElementModel.TagsRole:
                return item.tags
            elif role == ElementModel.NameRole:
                return item.colors
            elif role == QtCore.Qt.DisplayRole:
                return item.name
            elif role == QtCore.Qt.TextAlignmentRole:
                return QtCore.Qt.AlignCenter


class ExplorerSortModel(QtGui.QSortFilterProxyModel):

    def __init__(self, *args, **kwargs):
        super(ExplorerSortModel, self).__init__(*args, **kwargs)
        self._patterns = {}
        self.setDynamicSortFilter(True)
        self.setSourceModel(ElementModel())
        self.sort(0, QtCore.Qt.AscendingOrder)


    def set_pattern(self, role, value):
        self._patterns[role] = value


    def lessThan(self, left, right):
        leftData = self.sourceModel()._items[left.row()]
        rightData = self.sourceModel()._items[right.row()]

        if leftData and rightData:
            l = getattr(leftData, 'name', '')
            r = getattr(rightData, 'name', '')
            return l > r

        return True


    def filterAcceptsRow(self, sourceRow, sourceParent):
        sm = self.sourceModel()
        ix = sm.index(sourceRow)
        if ix.isValid():
            val = True
            for role, fvalue in self._patterns.items():
                value = ix.data(role)
                val = val and self.filter(value, fvalue, role)
            return val
        return False


    @staticmethod
    def filter(value, fvalue, role):
        '''
        fvalue: search value
        value: properties value being tested
        '''
        if role == ElementModel.TagsRole:
            if fvalue == []:
                return True
            else:
                return all(any(x in y for y in value) for x in fvalue)
        elif role == ElementModel.NameRole:
            return True
        else:
            return False


class QExplorerWidget(QtGui.QWidget):

    def __init__(self, *args, **kwargs):
        super(QExplorerWidget, self).__init__(*args, **kwargs)
        self.resize(400,400)

        # control
        self.ui_explorer = QtGui.QListView()
        self.ui_explorer.setResizeMode(QtGui.QListView.Adjust)
        self.ui_explorer.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
        self.ui_explorer.setMovement(QtGui.QListView.Static)
        self.ui_explorer.setSpacing(10)

        self.explorer_model = ExplorerSortModel()
        self.ui_explorer.setModel(self.explorer_model)

        self.ui_explorer_selection = self.ui_explorer.selectionModel()

        lay = QtGui.QVBoxLayout()
        lay.addWidget(self.ui_explorer)
        self.setLayout(lay)

        # connections
        self.ui_explorer_selection.selectionChanged.connect(self.changed_selection)

        # test data
        self.explorer_model.sourceModel().addItem(ExplorerItem('John',['john','sports']))
        self.explorer_model.sourceModel().addItem(ExplorerItem('Apple',['apple','fruit']))
        self.explorer_model.sourceModel().addItem(ExplorerItem('Kevin',['kevin','money']))
        self.explorer_model.sourceModel().addItem(ExplorerItem('Zoo',['zoo','animals']))


    def changed_selection(self):
        indexes =  self.ui_explorer_selection.selectedIndexes()
        for index in indexes:
            item = self.explorer_model.sourceModel().getItem(index)
            print item.name, item.tags, index


if __name__ == '__main__':
    ''
    app = QtGui.QApplication(sys.argv)
    ex = QExplorerWidget()
    ex.show()
    sys.exit(app.exec_())

selectedIndexesQModelIndex属于视图中建立的模型,本例是ExplorerSortModel,所以不能直接传递这些索引到 ElementModel 的 getItem() 方法,因为该方法期望 QModelIndex 属于 ElementModel.

在您的情况下,您必须使用 mapSource() 方法将属于 ExplorerSortModelQModelIndex 转换为属于 ElementModel 的相应 QModelIndex

def changed_selection(self):
    indexes =  self.ui_explorer_selection.selectedIndexes()
    for index in indexes:
        ix_source = self.explorer_model.mapToSource(index)
        item = self.explorer_model.sourceModel().getItem(ix_source)
        print(item.name, item.tags)