Skip to content Skip to sidebar Skip to footer

Use A Qsortfilterproxymodel From Qml With Pyqt5

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 bac

Solution 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:

classMyItem(QObject):

    nameChanged = pyqtSignal()

    def__init__(self, name, parent=None):
        QObject.__init__(self, parent)
        self._name = name

    @pyqtProperty('QString', notify=nameChanged)defname(self):
        return self._name

classMyModel(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 = 1defroleNames(self):
        print("roleNames")
        return self._roles

    defrowCount(self, parent=QModelIndex()):
        print("rowCount", len(self._items))
        returnlen(self._items)

    defdata(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

classSortFilterProxyModel(QSortFilterProxyModel):

    def__init__(self, parent):
        super().__init__(parent)

    @pyqtProperty(QAbstractItemModel)defsource (self):
        return self._source

    @source.setterdefsource (self, source):
        self.setSourceModel(source)
        self._source = source

    defroleKey(self, role):
        roles = self.roleNames()
        for key, value in roles.items():
            if value == role:
                return key
        return -1    @pyqtSlot(str, int)defsort(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:proxyModelsource:mymodel
}

TableView {
    id:tableViewanchors.fill:parentmodel:proxyModelsortIndicatorVisible:trueonSortIndicatorOrderChanged:model.sort(getColumn(sortIndicatorColumn).role, sortIndicatorOrder)onSortIndicatorColumnChanged:model.sort(getColumn(sortIndicatorColumn).role, sortIndicatorOrder)TableViewColumn {
        title:"Name"role:"name"
    }
}

Solution 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.

classMyItem(QObject):

    nameChanged = pyqtSignal()

    def__init__(self, name, parent=None):
        QObject.__init__(self, parent)
        self._name = name

    @pyqtProperty('QString', notify=nameChanged)defname(self):
        return self._name


classMyModel(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 = 1defroleNames(self):
        return self._roles

    defrowCount(self, parent=QModelIndex()):
        returnlen(self._items)

    defdata(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

classSortFilterProxyModel(QSortFilterProxyModel):

    classFilterSyntax:
        RegExp, Wildcard, FixedString = range(3)

    Q_ENUMS(FilterSyntax)

    def__init__(self, parent):
        super().__init__(parent)

    @pyqtProperty(QAbstractItemModel)defsource(self):
        returnsuper().sourceModel()

    @source.setterdefsource(self, source):
        self.setSourceModel(source)

    @pyqtProperty(int)defsortOrder(self):
        return self._order

    @sortOrder.setterdefsortOrder(self, order):
        self._order = order
        super().sort(0, order)

    @pyqtProperty(QByteArray)defsortRole(self):
        return self._roleNames().get(super().sortRole())

    @sortRole.setterdefsortRole(self, role):
        super().setSortRole(self._roleKey(role))

    @pyqtProperty(QByteArray)deffilterRole(self):
        return self._roleNames().get(super().filterRole())

    @filterRole.setterdeffilterRole(self, role):
        super().setFilterRole(self._roleKey(role))

    @pyqtProperty(str)deffilterString(self):
        returnsuper().filterRegExp().pattern()

    @filterString.setterdeffilterString(self, filter):
        super().setFilterRegExp(QRegExp(filter, super().filterCaseSensitivity(), self.filterSyntax))

    @pyqtProperty(int)deffilterSyntax(self):
        returnsuper().filterRegExp().patternSyntax()

    @filterSyntax.setterdeffilterSyntax(self, syntax):
        super().setFilterRegExp(QRegExp(self.filterString, super().filterCaseSensitivity(), syntax))

    deffilterAcceptsRow(self, sourceRow, sourceParent):
        rx = super().filterRegExp()
        ifnot rx or rx.isEmpty():
            returnTrue
        model = super().sourceModel()
        sourceIndex = model.index(sourceRow, 0, sourceParent)
        # skip invalid indexesifnot sourceIndex.isValid():
            returnTrue# If no filterRole is set, iterate through all keysifnot self.filterRole or self.filterRole == "":
            roles = self._roleNames()
            for key, value in roles.items():
                data = model.data(sourceIndex, key)
                if rx.indexIn(data) != -1:
                    returnTruereturnFalse# Here we have a filterRole set so only search in that
        data = model.data(sourceIndex, self._roleKey(self.filterRole))
        return rx.indexIn(data) != -1def_roleKey(self, role):
        roles = self.roleNames()
        for key, value in roles.items():
            if value == role:
                return key
        return -1def_roleNames(self):
        source = super().sourceModel()
        if source:
            return source.roleNames()
        return {}

Now I can do the following in QML

MyModel {
    id:mymodel
}

SortFilterProxyModel {
    id:proxyModelsource:mymodelsortOrder:tableView.sortIndicatorOrdersortCaseSensitivity:Qt.CaseInsensitivesortRole:tableView.getColumn(tableView.sortIndicatorColumn).rolefilterString:"*"+searchBox.text+"*"filterSyntax:SortFilterProxyModel.WildcardfilterCaseSensitivity:Qt.CaseInsensitivefilterRole:tableView.getColumn(tableView.sortIndicatorColumn).role
}

TableView {
    id:tableViewanchors.fill:parentmodel:proxyModelsortIndicatorVisible:trueTableViewColumn {
        role:"name"title:"Name"
    }
}

Post a Comment for "Use A Qsortfilterproxymodel From Qml With Pyqt5"