问题
I try to combine a QML view with a QSortFilterProxyModel within PyQt5. Unfortunately I can't get it to work in any way. 
My main problem right now seems to be to pass the items back from QML. But even if this would be possible it seems that it does not work, I get TypeError: QSortFilterProxyModel.setSourceModel(QAbstractItemModel): argument 1 has unexpected type 'PyCapsule' if I set the model directly in python.
Currently I have:
class SortFilterProxyModel(QSortFilterProxyModel):
    @pyqtProperty(QQmlListReference)
    def source (self):
        return self._source
    @source.setter
    def source (self, source):
        setSourceModel(source)
        self._source = source
class MyItem(QObject):
    nameChanged = pyqtSignal()
    def __init__(self, name, parent=None):
        QObject.__init__(self, parent)
        self._name = name
    @pyqtProperty('QString', notify=nameChanged)
    def name(self):
        return self._name
class MyModel(QObject):
    itemsChanged = pyqtSignal()
    def __init__(self, parent=None):
        QObject.__init__(self, parent)
        self._items = [MyItem('one'), MyItem('two'), MyItem('three')]
    @pyqtProperty(QQmlListProperty, notify=itemsChanged)
    def items(self):
        print('Query for items')
        return QQmlListProperty(MyItem, self, self._items)
    @pyqtSlot()
    def new_item(self):
        print('Append new item')
        self._items.append(MyItem('new'))
        self.itemsChanged.emit()
and
import QtQuick 2.2
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.2
import QtQuick.Dialogs 1.2
import MyModel 1.0
import MyItem 1.0
import SortFilterProxyModel 1.0
ApplicationWindow {
    function getCurrentIndex(list, element) {
        console.log('getCurrentIndex')
        if (list && element) {
            for (var i = 0; i < list.length; i++) {
                if (list[i].name === element.name) {
                    console.log('Found item at pos: ' + i)
                    return i
                }
            }
        }
        return -1
    }
    id: mainWindow
    width: 800; height: 600
    color: "gray"
    MyModel {
        id: mymodel
    }
    SortFilterProxyModel {
        id: proxyModel
        source: mymodel.items
    }
    TableView {
        anchors.fill: parent
        //model: mymodel.items
        model: proxyModel
        TableViewColumn {
            role: "name"
            title: "Name"
        }
    }
}
full source here:
https://github.com/sturmf/python_samples/tree/master/pyqt_qsortfilterproxymodel
回答1:
The only way to get it working was to switch to a QAbstractListModel as was suggested by Frank. Here is the important part of the code:
class MyItem(QObject):
    nameChanged = pyqtSignal()
    def __init__(self, name, parent=None):
        QObject.__init__(self, parent)
        self._name = name
    @pyqtProperty('QString', notify=nameChanged)
    def name(self):
        return self._name
class MyModel(QAbstractListModel):
    NameRole = Qt.UserRole + 1
    _roles = {NameRole: "name"}
    def __init__(self, parent=None):
        print("constructing")
        super().__init__(parent)
        self._items = [MyItem('one'), MyItem('two'), MyItem('three')]
        self._column_count = 1
    def roleNames(self):
        print("roleNames")
        return self._roles
    def rowCount(self, parent=QModelIndex()):
        print("rowCount", len(self._items))
        return len(self._items)
    def data(self, index, role=Qt.DisplayRole):
        print("in data")
        try:
            item = self._items[index.row()]
        except IndexError:
            return QVariant()
        if role == self.NameRole:
            return item.name
        return QVariant()
and a SortFilterProxyModel which I can use from QML
class SortFilterProxyModel(QSortFilterProxyModel):
    def __init__(self, parent):
        super().__init__(parent)
    @pyqtProperty(QAbstractItemModel)
    def source (self):
        return self._source
    @source.setter
    def source (self, source):
        self.setSourceModel(source)
        self._source = source
    def roleKey(self, role):
        roles = self.roleNames()
        for key, value in roles.items():
            if value == role:
                return key
        return -1
    @pyqtSlot(str, int)
    def sort(self, role, order):
        self.setSortRole(self.roleKey(role));
        super().sort(0, order);
Now I can do the following in QML
MyModel {
    id: mymodel
}
SortFilterProxyModel {
    id: proxyModel
    source: mymodel
}
TableView {
    id: tableView
    anchors.fill: parent
    model: proxyModel
    sortIndicatorVisible: true
    onSortIndicatorOrderChanged: model.sort(getColumn(sortIndicatorColumn).role, sortIndicatorOrder)
    onSortIndicatorColumnChanged: model.sort(getColumn(sortIndicatorColumn).role, sortIndicatorOrder)
    TableViewColumn {
        title: "Name"
        role: "name"
    }
}
回答2:
I now also implemented another way as described here: http://blog.qt.io/blog/2014/04/16/qt-weekly-6-sorting-and-filtering-a-tableview/
The implementation in the SortFilterProxyModel is a bit longer but the QML source gets imho nicer. This version also includes a filter implementation which makes it longer too.
class MyItem(QObject):
    nameChanged = pyqtSignal()
    def __init__(self, name, parent=None):
        QObject.__init__(self, parent)
        self._name = name
    @pyqtProperty('QString', notify=nameChanged)
    def name(self):
        return self._name
class MyModel(QAbstractListModel):
    NameRole = Qt.UserRole + 1
    _roles = {NameRole: "name"}
    def __init__(self, parent=None):
        super().__init__(parent)
        self._items = [MyItem('one'), MyItem('two'), MyItem('three')]
        self._column_count = 1
    def roleNames(self):
        return self._roles
    def rowCount(self, parent=QModelIndex()):
        return len(self._items)
    def data(self, index, role=Qt.DisplayRole):
        try:
            item = self._items[index.row()]
        except IndexError:
            return QVariant()
        if role == self.NameRole:
            return item.name
        return QVariant()
and a SortFilterProxyModel which I can use from QML
class SortFilterProxyModel(QSortFilterProxyModel):
    class FilterSyntax:
        RegExp, Wildcard, FixedString = range(3)
    Q_ENUMS(FilterSyntax)
    def __init__(self, parent):
        super().__init__(parent)
    @pyqtProperty(QAbstractItemModel)
    def source(self):
        return super().sourceModel()
    @source.setter
    def source(self, source):
        self.setSourceModel(source)
    @pyqtProperty(int)
    def sortOrder(self):
        return self._order
    @sortOrder.setter
    def sortOrder(self, order):
        self._order = order
        super().sort(0, order)
    @pyqtProperty(QByteArray)
    def sortRole(self):
        return self._roleNames().get(super().sortRole())
    @sortRole.setter
    def sortRole(self, role):
        super().setSortRole(self._roleKey(role))
    @pyqtProperty(QByteArray)
    def filterRole(self):
        return self._roleNames().get(super().filterRole())
    @filterRole.setter
    def filterRole(self, role):
        super().setFilterRole(self._roleKey(role))
    @pyqtProperty(str)
    def filterString(self):
        return super().filterRegExp().pattern()
    @filterString.setter
    def filterString(self, filter):
        super().setFilterRegExp(QRegExp(filter, super().filterCaseSensitivity(), self.filterSyntax))
    @pyqtProperty(int)
    def filterSyntax(self):
        return super().filterRegExp().patternSyntax()
    @filterSyntax.setter
    def filterSyntax(self, syntax):
        super().setFilterRegExp(QRegExp(self.filterString, super().filterCaseSensitivity(), syntax))
    def filterAcceptsRow(self, sourceRow, sourceParent):
        rx = super().filterRegExp()
        if not rx or rx.isEmpty():
            return True
        model = super().sourceModel()
        sourceIndex = model.index(sourceRow, 0, sourceParent)
        # skip invalid indexes
        if not sourceIndex.isValid():
            return True
        # If no filterRole is set, iterate through all keys
        if not self.filterRole or self.filterRole == "":
            roles = self._roleNames()
            for key, value in roles.items():
                data = model.data(sourceIndex, key)
                if rx.indexIn(data) != -1:
                    return True
            return False
        # Here we have a filterRole set so only search in that
        data = model.data(sourceIndex, self._roleKey(self.filterRole))
        return rx.indexIn(data) != -1
    def _roleKey(self, role):
        roles = self.roleNames()
        for key, value in roles.items():
            if value == role:
                return key
        return -1
    def _roleNames(self):
        source = super().sourceModel()
        if source:
            return source.roleNames()
        return {}
Now I can do the following in QML
MyModel {
    id: mymodel
}
SortFilterProxyModel {
    id: proxyModel
    source: mymodel
    sortOrder: tableView.sortIndicatorOrder
    sortCaseSensitivity: Qt.CaseInsensitive
    sortRole: tableView.getColumn(tableView.sortIndicatorColumn).role
    filterString: "*" + searchBox.text + "*"
    filterSyntax: SortFilterProxyModel.Wildcard
    filterCaseSensitivity: Qt.CaseInsensitive
    filterRole: tableView.getColumn(tableView.sortIndicatorColumn).role
}
TableView {
    id: tableView
    anchors.fill: parent
    model: proxyModel
    sortIndicatorVisible: true
    TableViewColumn {
        role: "name"
        title: "Name"
    }
}
来源:https://stackoverflow.com/questions/36823456/use-a-qsortfilterproxymodel-from-qml-with-pyqt5