2016-04-24 337 views
1

我嘗試在PyQt5中將QML視圖與QSortFilterProxyModel結合起來。不幸的是,我無法以任何方式讓它工作。 我現在的主要問題似乎是將項目從QML傳回。但即使這可能,它似乎不起作用,我得到TypeError: QSortFilterProxyModel.setSourceModel(QAbstractItemModel): argument 1 has unexpected type 'PyCapsule',如果我直接在python中設置模型。在PyQt5中使用QML中的QSortFilterProxyModel

目前我有:

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

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" 
     } 
    } 

} 

完整的源代碼在這裏:

https://github.com/sturmf/python_samples/tree/master/pyqt_qsortfilterproxymodel

+0

以下一個QSortFilterProxyModel的源模型必須是一個化QAbstractItemModel,其中QQmlListProperty和QQmlListReference不是派生自 –

回答

0

我現在還實現另一種方式,如下所述:http://blog.qt.io/blog/2014/04/16/qt-weekly-6-sorting-and-filtering-a-tableview/

Sort中的實現FilterProxyModel稍長一點,但QML源代碼更好。該版本還包括一個過濾器實現 ,這也使得它更長。

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

而且,我可以從QML

使用SortFilterProxyModel
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 {} 

現在我能做的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" 
    } 
} 
0

得到它的工作的唯一辦法是切換到QAbstractListModel作爲由弗蘭克建議。這裏是代碼的重要組成部分:

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

和SortFilterProxyModel我可以使用從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); 

現在我可以做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" 
    } 
}