我试图用QTableView中的1个QLineEdit框过滤多个列。我正在使用QAbstractTableModel进行过滤,并且我将QSortFilterProxyModel子类化以处理多个列。数据是从xlsx文件中提取的,并使用熊猫加载到数据框中。
因此,只要excel文件中的标头是数字(0、1、2..etc),我的代码就可以正常工作。在熊猫to_excel函数中,有一个标志可以防止熊猫分别添加其自己的“标头”和“索引”标签header = none和index = none。因此,当我将标题设置为none时,在填充QTableView时会出现此错误
TypeError: 'PySide2.QtCore.QRegExp.indexIn' called with wrong argument types:
PySide2.QtCore.QRegExp.indexIn(int)
Supported signatures:
PySide2.QtCore.QRegExp.indexIn(str, int = 0, PySide2.QtCore.QRegExp.CaretMode = PySide2.QtCore.QRegExp.CaretMode.CaretAtZero)
这是我自定义的“ filterAcceptsRow”函数:
def filterAcceptsRow(self, sourceRow, sourceParent):
index0 = self.sourceModel().index(sourceRow, 0, sourceParent)
index1 = self.sourceModel().index(sourceRow, 1, sourceParent)
index2 = self.sourceModel().index(sourceRow, 2, sourceParent)
# The following line causes the error
return ((self.filterRegExp().indexIn(self.sourceModel().data(index0, self.role)) >= 0
or self.filterRegExp().indexIn(self.sourceModel().data(index1, self.role)) >= 0
or self.filterRegExp().indexIn(self.sourceModel().data(index2, self.role)) >= 0))
这是我的代码的有效示例
from pandas import DataFrame, ExcelFile, Series
from PySide2.QtWidgets import QApplication, QTabWidget, QVBoxLayout, QAbstractItemView, QSizePolicy, QAbstractScrollArea, QHeaderView, QTableView, QMainWindow, QGridLayout, QLineEdit, QWidget
from PySide2.QtCore import Qt, QAbstractTableModel, QSortFilterProxyModel, QModelIndex, QRegExp, QDate, QDateTime
class PandasModel(QAbstractTableModel):
def __init__(self, data, parent=None):
QAbstractTableModel.__init__(self, parent)
self._filters = {}
self._sortBy = []
self._sortDirection = []
self._dfSource = data
self._dfDisplay = data
self._dfDisplay = self._dfDisplay.fillna("")
def rowCount(self, parent=QModelIndex()):
if parent.isValid():
return 0
return self._dfDisplay.shape[0]
def columnCount(self, parent=QModelIndex()):
if parent.isValid():
return 0
return self._dfDisplay.shape[1]
def data(self, index, role):
if index.isValid() and role == Qt.DisplayRole:
return self._dfDisplay.values[index.row()][index.column()]
return None
def headerData(self, col, orientation=Qt.Horizontal, role=Qt.DisplayRole):
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
return str(self._dfDisplay.columns[col])
return None
def setupModel(self, header, data):
self._dfSource = DataFrame(data, columns=header)
self._sortBy = []
self._sortDirection = []
self.setFilters({})
def setFilters(self, filters):
self.modelAboutToBeReset.emit()
self._filters = filters
print(filters)
self.updateDisplay()
self.modelReset.emit()
def sort(self, col, order=Qt.AscendingOrder):
# Storing persistent indexes
self.layoutAboutToBeChanged.emit()
oldIndexList = self.persistentIndexList()
oldIds = self._dfDisplay.index.copy()
# Sorting data
column = self._dfDisplay.columns[col]
ascending = (order == 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()
# Updating persistent indexes
newIds = self._dfDisplay.index
newIndexList = []
for index in oldIndexList:
id = oldIds[index.row()]
newRow = newIds.get_loc(id)
newIndexList.append(self.index(newRow, index.column(), index.parent()))
self.changePersistentIndexList(oldIndexList, newIndexList)
self.layoutChanged.emit()
self.dataChanged.emit(QModelIndex(), QModelIndex())
def updateDisplay(self):
dfDisplay = self._dfSource.copy()
# Filtering
cond = 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
class SortFilterProxyModel(QSortFilterProxyModel):
def __init__(self, data, parent=None):
super(SortFilterProxyModel, self).__init__(parent)
self.role = Qt.DisplayRole
self.minDate = QDate()
self.maxDate = QDate()
self.__data = data
def setFilterMinimumDate(self, date):
self.minDate = date
self.invalidateFilter()
def filterMinimumDate(self):
return self.minDate
def setFilterMaximumDate(self, date):
self.maxDate = date
self.invalidateFilter()
def filterMaximumDate(self):
return self.maxDate
def filterAcceptsRow(self, sourceRow, sourceParent):
index0 = self.sourceModel().index(sourceRow, 0, sourceParent)
index1 = self.sourceModel().index(sourceRow, 1, sourceParent)
index2 = self.sourceModel().index(sourceRow, 2, sourceParent)
return ((self.filterRegExp().indexIn(self.sourceModel().data(index0, self.role)) >= 0 or self.filterRegExp().indexIn(self.sourceModel().data(index1, self.role)) >= 0 or self.filterRegExp().indexIn(self.sourceModel().data(index2, self.role)) >= 0))
def lessThan(self, left, right):
leftData = self.sourceModel().data(left, self.role)
rightData = self.sourceModel().data(right, self.role)
if not isinstance(leftData, QDate):
emailPattern = QRegExp("([\\w\\.]*@[\\w\\.]*)")
if left.column() == 1 and emailPattern.indexIn(leftData) != -1:
leftData = emailPattern.cap(1)
if right.column() == 1 and emailPattern.indexIn(rightData) != -1:
rightData = emailPattern.cap(1)
return leftData < rightData
def dateInRange(self, date):
if isinstance(date, QDateTime):
date = date.date()
return ((not self.minDate.isValid() or date >= self.minDate)
and (not self.maxDate.isValid() or date <= self.maxDate))
class PerfSearchTest(QMainWindow):
def __init__(self):
super().__init__()
self.path_to_local_spreadsheet = "output.xlsx"
self.spreadsheet_display = {}
self.searches = {}
self.current_tab_layout = {}
self.tab_widget = {}
self.central_widget = QWidget()
self.tabs = QTabWidget()
self.layout_grid = QGridLayout(self)
self.layout_grid.setSpacing(10)
self.populate_spreadsheet_table()
self.layout_grid.addWidget(self.tabs, 0, 0)
self.central_widget.setLayout(self.layout_grid)
self.setCentralWidget(self.central_widget)
def onTextChanged(self, text):
self.proxyModelContact.setFilterRegExp(QRegExp(text, Qt.CaseInsensitive, QRegExp.FixedString))
def populate_spreadsheet_table(self):
opened_excel_flie_dataframe = ExcelFile(self.path_to_local_spreadsheet)
opened_excel_file_dataframe_worksheets = []
for y, sheet in enumerate(opened_excel_flie_dataframe.sheet_names):
df = opened_excel_flie_dataframe.parse(y)
self.tab_widget[y] = QWidget()
self.searches[y] = QLineEdit(sheet)
self.spreadsheet_display[y] = QTableView()
self.spreadsheet_display[y].setSortingEnabled(True)
self.spreadsheet_display[y].setEditTriggers(QAbstractItemView.NoEditTriggers)
self.spreadsheet_display[y].setAlternatingRowColors(True)
self.spreadsheet_display[y].horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
self.spreadsheet_display[y].verticalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
self.spreadsheet_display[y].setSizeAdjustPolicy(QAbstractScrollArea.AdjustToContents)
self.spreadsheet_display[y].setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Expanding)
self.spreadsheet_display[y].resizeColumnsToContents()
self.current_tab_layout[y] = QVBoxLayout()
self.current_tab_layout[y].addWidget(self.searches[y])
self.current_tab_layout[y].addWidget(self.spreadsheet_display[y])
self.proxyModelContact = SortFilterProxyModel(df)
self.proxyModelContact.setSourceModel(PandasModel(df))
self.searches[y].textChanged.connect(self.onTextChanged)
self.spreadsheet_display[y].setModel(self.proxyModelContact)
self.tab_widget[y].setLayout(self.current_tab_layout[y])
opened_excel_file_dataframe_worksheets.append(df)
self.tabs.addTab(self.tab_widget[y], sheet)
if __name__ == "__main__":
app = QApplication()
w = PerfSearchTest()
w.show()
app.exec_()
Here's a 'broken' spreadsheet that will replicate the error
Here's a 'working' spreadsheet that will not replicate the error(or it shouldn't)
如果需要,我很乐意传递整个源代码,以防万一。我没有包括依赖pygsheets下载的内容-输入到pandas数据框-然后导出到xslx。
答案 0 :(得分:1)
引起此问题的原因是indexIng需要一个字符串,并且如您所指出的,您正在传递一个整数,因此可能的解决方案是将其转换为字符串:
def filterAcceptsRow(self, sourceRow, sourceParent):
index0 = self.sourceModel().index(sourceRow, 0, sourceParent)
index1 = self.sourceModel().index(sourceRow, 1, sourceParent)
index2 = self.sourceModel().index(sourceRow, 2, sourceParent)
value = self.sourceModel().data(index0, self.role)
for ix in (index0, index1, index2):
value = self.sourceModel().data(ix, self.role)
if self.filterRegExp().indexIn(str(value)) >= 0:
return True
return False