如何使用 QAbstractTableModel 而不是 QSortFilterProxyModel 进行排序和过滤
Posted
技术标签:
【中文标题】如何使用 QAbstractTableModel 而不是 QSortFilterProxyModel 进行排序和过滤【英文标题】:How to sort and filter using QAbstractTableModel instead of 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_())
【问题讨论】:
【参考方案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_())
【讨论】:
以上是关于如何使用 QAbstractTableModel 而不是 QSortFilterProxyModel 进行排序和过滤的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 beginMoveRows 在 QTableView (QAbstractTableModel) 中移动一行?
使用 QAbstractTableModel(模型/视图)时如何将“选择一个...”添加到 QComboBox?
QAbstractTableModel 和 QSortFilterProxyModel - 如何清除数据和更新视图
使用排序的 QAbstractTableModel 时如何将“选择项目...”添加到 QComboBox?