【问题标题】:How to sort and filter using QAbstractTableModel instead of QSortFilterProxyModel如何使用 QAbstractTableModel 而不是 QSortFilterProxyModel 进行排序和过滤
【发布时间】:2015-08-22 06:01:42
【问题描述】:

我尝试通过将这个属性与当前显示在 QComboBox 中的文本进行比较,从 QAbstractTableModel 的 data() 方法内部通过 self.category 属性过滤 self.items 的对象。但是,代码无法正常运行。

不应该将 QAbstractTableModel 的 data() 方法用作“代理模型的accepts row() 方法的替代品吗?

不使用QSortFilterProxyModel是否可以实现过滤?如果我们必须使用代理来过滤模型项,那么最 Pythonic 的方式是什么?

from PySide import QtGui, QtCore

class Item(object):
    def __init__(self):
        self.ID=None
        self.name=None
        self.category=None

class TableModel(QtCore.QAbstractTableModel):
    def __init__(self, parent=None):
        QtCore.QAbstractTableModel.__init__(self, parent)
        self.items = []
        self.filterCategory = None

    def rowCount(self, parent=QtCore.QModelIndex()):
        return len( [item for item in self.items if item.category==self.filterCategory] )

    def columnCount(self, parent=QtCore.QModelIndex()):
        return 1

    def data(self, index, role):
        if not index.isValid(): return 
        row=index.row()

        item=self.items[row]
        if item.category!=self.filterCategory:
            return  

        if role == QtCore.Qt.DisplayRole:
            return self.items[row].name

        if role == QtCore.Qt.UserRole:
            return self.items[row]

    def insertRows(self, row, item, column=1, index=QtCore.QModelIndex()):
        self.beginInsertRows(QtCore.QModelIndex(), row, row+1)
        self.items.append(item)
        self.endInsertRows()

    def setFilter(self, comboText):
        self.filterCategory = comboText
        self.layoutChanged.emit()

    def filterAcceptsRow(self, row, proc):
        index=self.sourceModel().index(row, 0, proc)
        item=self.sourceModel().data(index, QtCore.Qt.UserRole)
        if not item: return True

        resourceType=item.category
        if self.filters.get(category)==False:
            return False       

        if self.searchText and len(self.searchText)>0 and item.searchString(self.searchText)==False:
            return False
        return True

class MyWindow(QtGui.QWidget):
    def __init__(self, *args):
        QtGui.QWidget.__init__(self, *args)
        vLayout=QtGui.QVBoxLayout(self)
        self.setLayout(vLayout)

        self.tableModel = TableModel()     

        self.ViewA=QtGui.QTableView(self)
        self.ViewA.clicked.connect(self.viewClicked)
        vLayout.addWidget(self.ViewA)

        for row in range(5):
            item=Item()
            item.ID=row
            if item.ID%2: item.category='Pet'
            else: item.category='Birds'
            item.name='%s_%s'%(item.category, row)

            self.tableModel.insertRows(row, item)

        self.ViewA.setModel(self.tableModel)

        self.combo=QtGui.QComboBox()
        self.combo.addItems(['Pet','Birds'])
        self.combo.activated.connect(self.comboActivated)
        vLayout.addWidget(self.combo)

        currentComboCategory=self.combo.currentText()
        self.tableModel.setFilter(currentComboCategory)


    def viewClicked(self, indexClicked):
        print('indexClicked() row: %s  column: %s'%(indexClicked.row(), indexClicked.column() ))

    def comboActivated(self, arg=None):
        comboText=self.combo.currentText()
        self.tableModel.setFilter(comboText)

if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    w = MyWindow()
    w.show()
    sys.exit(app.exec_())

【问题讨论】:

    标签: python qt model pyqt qsortfilterproxymodel


    【解决方案1】:

    下面的“no-more-proxy”代码展示了如何从QAbstractTableModel内部而不是代理内部进行排序和过滤:

    import sys, os
    from PyQt import QtGui, QtCore
    
    class Item(object):
        def __init__(self,ID=None,name=None,category=None,area=None):
            self.ID=ID
            self.name=name
            self.category=category
            self.area='South'
    
    class TableModel(QtCore.QAbstractTableModel):
        def __init__(self, parent=None):
            QtCore.QAbstractTableModel.__init__(self, parent)
            self.currentItems=[]
            self.items = []
            self.filterCategory = None
            self.searchField = None
    
            self.mainColumn=0
            self.order=QtCore.Qt.SortOrder.DescendingOrder
    
        def rowCount(self, parent=QtCore.QModelIndex()):
            return len( self.currentItems )
    
        def columnCount(self, parent=QtCore.QModelIndex()):
            return 5
    
        def data(self, index, role):
            if not index.isValid(): return 
            row=index.row()
            column=index.column()
    
            item=self.currentItems[row]
    
            if role == QtCore.Qt.DisplayRole:
                if column==0: return item.ID
                elif column==1: return item.name
                elif column==2: return item.category
                elif column==4 or column==5: return item.area
    
            if role == QtCore.Qt.UserRole:
                return item
    
        def insertRows(self, row, item, column=1, index=QtCore.QModelIndex()):
            self.beginInsertRows(QtCore.QModelIndex(), row, row+1)
            self.items.append(item)
            self.endInsertRows()
    
        def setFilter(self, comboText=None, searchText=None, mainColumn=None, order=None):
            if comboText: self.filterCategory=comboText
            if searchText: self.searchText=searchText
            if mainColumn!=None: self.mainColumn=mainColumn
            self.order=order
    
            self.currentItems=[item for item in self.items if item.category==self.filterCategory]
            if searchText:
                self.currentItems=[item for item in self.currentItems if searchText in '%s%s%s'%(item.ID, item.name, item.category)]
    
            values=[]
            if self.mainColumn==0: values=[[item.ID, item, False] for item in self.currentItems]
            elif self.mainColumn==1: values=[[item.name, item, False] for item in self.currentItems]
            elif self.mainColumn==2: values=[[item.category, item, False] for item in self.currentItems]
            elif self.mainColumn==3 or self.mainColumn==4: values=[[item.area, item, False] for item in self.currentItems]  
    
            keys=sorted([value[0] for value in values if isinstance(value, list)])
            if self.order==QtCore.Qt.AscendingOrder: keys=list(reversed(keys))
    
            filtered=[]
            for key in keys:
                for each in values:
                    if each[0]!=key: continue
                    if each[2]==True: continue
                    item=each[1]
    
                    filtered.append(item)
                    each[2]=True
    
            if filtered: self.currentItems=filtered
    
            self.layoutChanged.emit()
    
    class ItemDelegate(QtGui.QItemDelegate):
        def __init__(self, parent):
            QtGui.QItemDelegate.__init__(self, parent)
    
        def flags(self, index):
            if (index.column() == 1):
                return QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled
            else:
                return QtCore.Qt.ItemIsEnabled
    
        def createEditor(self, parent, option, index):
            tableView=parent.parent()
            model=tableView.model()
    
            item=model.data(index, QtCore.Qt.UserRole)
    
            combo=QtGui.QComboBox(parent)
            combo.addItems(['South','West','North','East'])
            combo.currentIndexChanged.connect(self.comboIndexChanged)    
    
            if item.area:
                comboIndex=combo.findText(item.area)
                if comboIndex>=0:
                    combo.setCurrentIndex(comboIndex)
            else: combo.setCurrentIndex(0)
    
            return combo
    
        def comboIndexChanged(self):
            self.commitData.emit(self.sender())
    
        def setModelData(self, combo, model, index): 
            item=model.data(index, QtCore.Qt.UserRole)
            comboText=combo.currentText()
            item.area=comboText
    
    class MyWindow(QtGui.QWidget):
        def __init__(self, *args):
            QtGui.QWidget.__init__(self, *args)
            vLayout=QtGui.QVBoxLayout(self)
            self.setLayout(vLayout)
    
            self.tableModel = TableModel()   
    
            self.searchLine=QtGui.QLineEdit()
            vLayout.addWidget(self.searchLine)
    
            self.searchLine.textEdited.connect(self.searchLineEditied)
            self.searchLine.returnPressed.connect(self.searchLineEditied)
    
            self.tableView=QtGui.QTableView(self)
            self.tableView.setSortingEnabled(True)
            self.tableView.horizontalHeader().setResizeMode(QtGui.QHeaderView.Stretch) 
            self.tableView.setShowGrid(False) 
            self.tableView.setSelectionBehavior(QtGui.QTableView.SelectRows)
            self.tableView.setAlternatingRowColors(True)
    
            self.delegate=ItemDelegate(self.tableView)
            self.tableView.setItemDelegate(self.delegate)
    
            self.tableView.clicked.connect(self.viewClicked)
            vLayout.addWidget(self.tableView)
    
            for row in range(15):
                if row%2:  category='Pet' 
                else: category='Birds'
                item=Item(category=category, ID=row, name='%s_%s'%(category,row)) 
                self.tableModel.insertRows(row, item)
    
            self.tableView.setModel(self.tableModel)
    
            self.combo=QtGui.QComboBox()
            self.combo.addItems(['Pet','Birds'])
            self.combo.activated.connect(self.comboActivated)
            vLayout.addWidget(self.combo)
    
            currentComboCategory=self.combo.currentText()
            self.tableModel.setFilter(currentComboCategory)
    
            self.horizontalHeader=self.tableView.horizontalHeader()
            self.horizontalHeader.sortIndicatorChanged.connect(self.headerTriggered)
            self.addComboDelegates()
    
        def headerTriggered(self, mainColumn=None, order=None):
            self.tableModel.setFilter(mainColumn=mainColumn, order=order)
            self.deleteComboDelegates()
            self.addComboDelegates() 
    
        def comboActivated(self, comboIndex=None):
            self.deleteComboDelegates()
            comboText=self.combo.currentText()
            self.tableModel.setFilter(comboText=comboText)
            self.addComboDelegates()
            self.tableModel.layoutChanged.emit()
    
        def searchLineEditied(self, searchText=None):
            self.tableModel.setFilter(searchText=searchText)
    
        def viewClicked(self, indexClicked):
            item=self.tableModel.data(indexClicked, QtCore.Qt.UserRole)
            print 'ID: %s, name: %s, category: %s'%(item.ID,item.name,item.category)
    
        def deleteComboDelegates(self):
            for row in range(self.tableModel.rowCount()):
                index=self.tableModel.index(row, 3, QtCore.QModelIndex())
                self.tableView.closePersistentEditor(index)
    
        def addComboDelegates(self):
            for row in range(self.tableModel.rowCount()):
                index=self.tableModel.index(row, 3, QtCore.QModelIndex())
                self.tableView.openPersistentEditor(index)
    
    
    if __name__ == "__main__":
        app = QtGui.QApplication(sys.argv)
        w = MyWindow()
        w.show()
        sys.exit(app.exec_())
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-04-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-04
      • 2020-02-10
      • 2021-10-14
      相关资源
      最近更新 更多