我正在使用PyQt5(5.7.1)和python3.5开发一个应用程序。我使用QTableView来显示一个很长的记录列表(超过10000条)。我想在同一时间对列表中的多个列进行排序。在
我尝试使用QAbstractTableModel和QSortFilterProxyModel,重新实现QSortFilterProxyModel.filterAcceptsRow()进行多列过滤(请参阅此博客文章:http://www.dayofthenewdan.com/2013/02/09/Qt_QSortFilterProxyModel.html)。但是,由于对每一行都调用此方法,所以当有大量行时,过滤非常慢。在
我认为使用Pandas进行过滤可以提高性能。因此,我创建了下面的pandablemodel类,它确实可以非常快速地执行多列过滤,甚至可以进行排序:
import pandas as pd
from PyQt5 import QtCore, QtWidgets
class PandasTableModel(QtCore.QAbstractTableModel):
def __init__(self, parent=None, *args):
super(PandasTableModel, self).__init__(parent, *args)
self._filters = {}
self._sortBy = []
self._sortDirection = []
self._dfSource = pd.DataFrame()
self._dfDisplay = pd.DataFrame()
def rowCount(self, parent=QtCore.QModelIndex()):
if parent.isValid():
return 0
return self._dfDisplay.shape[0]
def columnCount(self, parent=QtCore.QModelIndex()):
if parent.isValid():
return 0
return self._dfDisplay.shape[1]
def data(self, index, role):
if index.isValid() and role == QtCore.Qt.DisplayRole:
return QtCore.QVariant(self._dfDisplay.values[index.row()][index.column()])
return QtCore.QVariant()
def headerData(self, col, orientation=QtCore.Qt.Horizontal, role=QtCore.Qt.DisplayRole):
if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
return QtCore.QVariant(str(self._dfDisplay.columns[col]))
return QtCore.QVariant()
def setupModel(self, header, data):
self._dfSource = pd.DataFrame(data, columns=header)
self._sortBy = []
self._sortDirection = []
self.setFilters({})
def setFilters(self, filters):
self.modelAboutToBeReset.emit()
self._filters = filters
self.updateDisplay()
self.modelReset.emit()
def sort(self, col, order=QtCore.Qt.AscendingOrder):
#self.layoutAboutToBeChanged.emit()
column = self._dfDisplay.columns[col]
ascending = (order == QtCore.Qt.AscendingOrder)
if column in self._sortBy:
i = self._sortBy.index(column)
self._sortBy.pop(i)
self._sortDirection.pop(i)
self._sortBy.insert(0, column)
self._sortDirection.insert(0, ascending)
self.updateDisplay()
#self.layoutChanged.emit()
self.dataChanged.emit(QtCore.QModelIndex(), QtCore.QModelIndex())
def updateDisplay(self):
dfDisplay = self._dfSource.copy()
# Filtering
cond = pd.Series(True, index = dfDisplay.index)
for column, value in self._filters.items():
cond = cond & \
(dfDisplay[column].str.lower().str.find(str(value).lower()) >= 0)
dfDisplay = dfDisplay[cond]
# Sorting
if len(self._sortBy) != 0:
dfDisplay.sort_values(by=self._sortBy,
ascending=self._sortDirection,
inplace=True)
# Updating
self._dfDisplay = dfDisplay
这个类复制QSortFilterProxyModel的行为,除了一个方面。如果在QTableView中选择了表中的某个项目,则对该表进行排序不会影响选择(例如,如果在排序之前选择了第一行,则排序后仍将选择第一行,而不是与之前相同的行。在
我认为问题与发射的信号有关。对于过滤,我使用了modelAboutBoreSet()和modelReset(),但是这些信号会取消QTableView中的选择,因此它们不适合排序。我在那里读到(How to update QAbstractTableModel and QTableView after sorting the data source?)layoutBouttoBeChanged()和layoutChanged()应该被发出。但是,如果我使用这些信号,QTableView不会更新(实际上我不明白为什么)。当排序完成后发出dataChanged()时,QTableView会更新,但会使用上面描述的行为(选择未更新)。在
可以使用以下示例测试此模型:
^{pr2}$排序时如何使所选内容跟随所选元素?在
多亏了Ekhurvo,我找到了解决办法。sort函数应该存储持久索引,创建新的索引并更改它们。下面是这样做的代码。对大量记录进行排序似乎有点慢,但这是可以接受的。在
编辑:由于一个未知的原因,在最后发出改变的数据大大加快了排序速度。我试图用layoutBouttoBeChanged和layoutChanged(例如。self.layoutChanged.emit([], QtCore.QAbstractItemModel.VerticalSortHing)),但我得到一个错误,这些信号不带参数,考虑到Qt5文档中描述的这些信号的签名,这很奇怪。在
不管怎样,这段代码给了我预期的结果,所以已经是这样了。理解它的作用只是一个额外的收获!^^如果有人能解释的话,我很想知道。在
相关问题 更多 >
编程相关推荐