Python 通过QSqlRelationalTableModel中的QSortFilterProxyModel进行过滤时,QTreeView上的无休止循环
每当我使用Python 通过QSqlRelationalTableModel中的QSortFilterProxyModel进行过滤时,QTreeView上的无休止循环,python,pyqt5,qtreeview,qsortfilterproxymodel,qsqlrelationaltablemodel,Python,Pyqt5,Qtreeview,Qsortfilterproxymodel,Qsqlrelationaltablemodel,每当我使用QSortFilterProxyModel()启用筛选器并在链接到QTreeView的QSqlRelationalTableModel()中插入新记录时,我都会收到错误: RecursionError: maximum recursion depth exceeded 标准情况是使用CTRL+N-OK创建新的数据记录 过滤也可以-好的 但是如果我设置过滤器并创建新记录,python将失败: RecursionError: maximum recursion depth exceede
QSortFilterProxyModel()
启用筛选器并在链接到QTreeView
的QSqlRelationalTableModel()
中插入新记录时,我都会收到错误:
RecursionError: maximum recursion depth exceeded
标准情况是使用CTRL+N
-OK创建新的数据记录
过滤也可以-好的
但是如果我设置过滤器并创建新记录,python将失败:
RecursionError: maximum recursion depth exceeded
Backend terminated (returncode: 3)
Fatal Python error: Aborted
如何复制:
lastName
设置为Smith
CTRL+N
创建新记录import sys
import re
from PyQt5 import QtWidgets, QtGui, QtCore, QtSql
db = QtSql.QSqlDatabase.addDatabase("QSQLITE")
db.setDatabaseName(":memory:");
modelQuery = QtSql.QSqlQueryModel()
modelTable = QtSql.QSqlRelationalTableModel()
def _human_key(key):
parts = re.split(r'(\d*\.\d+|\d+)', key)
return tuple((e.swapcase() if i % 2 == 0 else float(e))
for i, e in enumerate(parts))
class FilterHeader(QtWidgets.QHeaderView):
filterActivated = QtCore.pyqtSignal()
def __init__(self, parent):
super().__init__(QtCore.Qt.Horizontal, parent)
self._editors = []
self._padding = 4
self.setStretchLastSection(True)
self.setDefaultAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
self.setSortIndicatorShown(False)
self.sectionResized.connect(self.adjustPositions)
parent.horizontalScrollBar().valueChanged.connect(self.adjustPositions)
def setFilterBoxes(self, count):
while self._editors:
editor = self._editors.pop()
editor.deleteLater()
for index in range(count):
editor = QtWidgets.QLineEdit(self.parent())
editor.setPlaceholderText('Filter')
editor.setClearButtonEnabled(True)
editor.textChanged.connect(self.textChanged)
self._editors.append(editor)
self.adjustPositions()
def textChanged(self):
self.filterActivated.emit()
def sizeHint(self):
size = super().sizeHint()
if self._editors:
height = self._editors[0].sizeHint().height()
size.setHeight(size.height() + height + self._padding)
return size
def updateGeometries(self):
if self._editors:
height = self._editors[0].sizeHint().height()
self.setViewportMargins(0, 0, 0, height + self._padding)
else:
self.setViewportMargins(0, 0, 0, 0)
super().updateGeometries()
self.adjustPositions()
def adjustPositions(self):
for index, editor in enumerate(self._editors):
height = editor.sizeHint().height()
editor.move(
self.sectionPosition(index) - self.offset() + 2,
height + (self._padding // 2))
editor.resize(self.sectionSize(index), height)
def filterText(self, index):
if 0 <= index < len(self._editors):
return self._editors[index].text()
return ''
def setFilterText(self, index, text):
if 0 <= index < len(self._editors):
self._editors[index].setText(text)
def clearFilters(self):
for editor in self._editors:
editor.clear()
class HumanProxyModel(QtCore.QSortFilterProxyModel):
def lessThan(self, source_left, source_right):
data_left = source_left.data()
data_right = source_right.data()
if type(data_left) == type(data_right) == str:
return _human_key(data_left) < _human_key(data_right)
return super(HumanProxyModel, self).lessThan(source_left, source_right)
@property
def filters(self):
if not hasattr(self, "_filters"):
self._filters = []
return self._filters
@filters.setter
def filters(self, filters):
self._filters = filters
self.invalidateFilter()
def filterAcceptsRow(self, sourceRow, sourceParent):
for i, text in self.filters:
if 0 <= i < self.columnCount():
ix = self.sourceModel().index(sourceRow, i, sourceParent)
data = ix.data()
if text not in data:
return False
return True
class winMain(QtWidgets.QMainWindow):
cur_row = -1
row_id = -1
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi()
self.setGeometry(300,200,700,500)
self.treeView.selectionModel().selectionChanged.connect(self.item_selection_changed_slot)
self.center()
self.show()
def new_dataset(self):
print("new_dataset() called.")
# get new row
row = modelTable.rowCount()
new_row = row+1
self.cur_row = new_row
# get next free row id
model = QtSql.QSqlQueryModel()
model.setQuery("SELECT max(id)+1 FROM person")
self.row_id = model.data(model.index(0, 0))
# insert a new row with dummy data
modelTable.insertRow(row)
modelTable.setData(modelTable.index(row,0), self.row_id, QtCore.Qt.EditRole)
modelTable.setData(modelTable.index(row,1), "new" + str(self.row_id), QtCore.Qt.EditRole)
modelTable.setData(modelTable.index(row,2), "new" + str(self.row_id), QtCore.Qt.EditRole)
modelTable.setData(modelTable.index(row,3), "new" + str(self.row_id), QtCore.Qt.EditRole)
modelTable.setData(modelTable.index(row,4), 2, QtCore.Qt.EditRole)
modelTable.submitAll()
def handleFilterActivated(self):
header = self.treeView.header()
filters = []
for i in range(header.count()):
text = header.filterText(i)
if text:
filters.append((i, text))
proxy = self.treeView.model()
proxy.filters = filters
QtCore.pyqtSlot()
def item_selection_changed_slot(self):
selected = self.treeView.selectionModel()
indexes = selected.selectedIndexes()
sourceIdx = self.treeView.currentIndex()
ix = self.treeView.model().index(sourceIdx.row(), 0) # column which contains the id
self.cur_row = sourceIdx.row()
self.row_id = ix.data()
record = modelTable.record(self.cur_row)
persId = record.value("persId")
lastName = record.value("lastName")
firstName = record.value("firstName")
country = record.value("name")
print(f"{persId} - {lastName}, {firstName} from {country} selected.")
def keyReleaseEvent(self, eventQKeyEvent):
key = eventQKeyEvent.key()
modifiers = QtWidgets.QApplication.keyboardModifiers()
if modifiers == QtCore.Qt.ShiftModifier and key == QtCore.Qt.Key_Escape:
self.clear_all_filters()
def center(self):
frameGm = self.frameGeometry()
screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center()
frameGm.moveCenter(centerPoint)
self.move(frameGm.topLeft())
def setupUi(self):
self.centralwidget = QtWidgets.QWidget(self)
self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
self.treeView = QtWidgets.QTreeView(self.centralwidget)
self.treeView.setRootIsDecorated(False)
self.treeView.setSortingEnabled(True)
self.treeView.setAlternatingRowColors(True)
self.treeView.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
self.treeView.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
self.treeView.header().setStretchLastSection(True)
self.horizontalLayout.addWidget(self.treeView)
self.setCentralWidget(self.centralwidget)
header = FilterHeader(self.treeView)
self.treeView.setHeader(header)
# ToolBar
newDatasetAct = QtWidgets.QAction(QtGui.QIcon('img/icons8-new-file-50.png'), 'New dataset (CTRL+N)', self)
newDatasetAct.setShortcut('Ctrl+N')
newDatasetAct.triggered.connect(self.new_dataset)
self.toolbar = self.addToolBar('Main')
self.toolbar.addAction(newDatasetAct)
modelTable.setTable("person")
modelTable.setRelation(4, QtSql.QSqlRelation("country", "id", "name"));
modelTable.setEditStrategy(QtSql.QSqlTableModel.OnManualSubmit)
self.treeView.setModel(modelTable) # display data of the SQLTableModel into the QTreeView
# enable human sorting
proxy = HumanProxyModel(self)
proxy.setSourceModel(modelTable)
self.treeView.setModel(proxy)
# enable filtering
header.setFilterBoxes(modelTable.columnCount())
header.filterActivated.connect(self.handleFilterActivated)
def create_sample_data():
modelQuery.setQuery("""CREATE TABLE IF NOT EXISTS country (
id INTEGER PRIMARY KEY UNIQUE NOT NULL,
name TEXT
)""")
# id INTEGER PRIMARY KEY UNIQUE,
modelQuery.setQuery("""CREATE TABLE IF NOT EXISTS person (
id INTEGER PRIMARY KEY UNIQUE NOT NULL,
persId TEXT,
lastName TEXT,
firstName TEXT,
country_id INTEGER NOT NULL DEFAULT 3,
FOREIGN KEY (country_id) REFERENCES country(id)
)""")
# create some sample data for our model
modelQuery.setQuery("INSERT INTO country (id, name) VALUES (0, 'None')")
modelQuery.setQuery("INSERT INTO country (id, name) VALUES (1, 'Angola')")
modelQuery.setQuery("INSERT INTO country (id, name) VALUES (2, 'Serbia')")
modelQuery.setQuery("INSERT INTO country (id, name) VALUES (3, 'Georgia')")
modelQuery.setQuery("INSERT INTO person (id, persId, lastName, firstName, country_id) VALUES (1, '1001', 'Martin', 'Robert', 1)")
modelQuery.setQuery("INSERT INTO person (id, persId, lastName, firstName, country_id) VALUES (2, '1002', 'Smith', 'Brad', 2)")
modelQuery.setQuery("INSERT INTO person (id, persId, lastName, firstName, country_id) VALUES (3, '1003', 'Smith', 'Angelina', 3)")
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
create_sample_data()
window = winMain()
sys.exit(app.exec_())
导入系统
进口稀土
从PyQt5导入QtWidgets、QtGui、QtCore、QtSql
db=QtSql.QSqlDatabase.addDatabase(“QSQLITE”)
db.setDatabaseName(“:内存:”);
modelQuery=QtSql.QSqlQueryModel()
modelTable=QtSql.QSqlRelationalTableModel()
def_人力_钥匙(钥匙):
parts=re.split(r'(\d*\.\d+\d+),键)
返回元组((如果i%2==0,则为e.swapcase(),否则为float(e))
用于列举(部分)中的i、e
类FilterHeader(qtwidts.QHeaderView):
filterActivated=QtCore.pyqtSignal()
定义初始化(自身,父级):
super()
self._编辑器=[]
自填充=4
自整定拉伸截面(真)
self.setDefaultAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
self.setSortIndicator显示(假)
自调整截面尺寸连接(自调整位置)
parent.horizontalScrollBar().valueChanged.connect(自调整位置)
def设置过滤器盒(自身,计数):
而self.\u编辑:
editor=self.\u editors.pop()
editor.deleteLater()
对于范围内的索引(计数):
editor=qtwidts.QLineEdit(self.parent())
editor.setplaceholder文本('Filter'))
编辑器.setClearButtonEnabled(True)
editor.textChanged.connect(self.textChanged)
self.\u editors.append(编辑器)
自我调整位置()
def textChanged(自我):
self.filterActivated.emit()
def sizeHint(自身):
size=super().sizeHint()
如果是self.\u编辑器:
高度=self.\u编辑器[0].sizeHint().height()
size.setHeight(size.height()+高度+自身填充)
返回大小
def更新计量(自我):
如果是self.\u编辑器:
高度=self.\u编辑器[0].sizeHint().height()
self.setViewportMargins(0,0,0,高度+自填充)
其他:
self.setViewportMargins(0,0,0,0)
super().updateGeometries()
自我调整位置()
def调整位置(自身):
对于索引,枚举中的编辑器(self.\u编辑器):
高度=编辑器.sizeHint().height()
编辑:移动(
self.section位置(索引)-self.offset()+2,
高度+(自._//2))
编辑器.调整大小(self.sectionSize(索引)、高度)
def过滤器文本(自身,索引):
如果0则问题来自对
filterAcceptsRow
中的self.columnCount()
的调用如果为0,则使用
,就是这样!精彩的!非常感谢你!你现在知道我花了多少时间在这上面了!也许你在这里也有一个想法@ProfP30不客气,是的,我知道在您完全理解模型的工作原理之前,跟踪这些问题可能有多困难(提示:在您不理解Qt正在做什么的时候查找)。关于另一个问题,我很抱歉,除非您能够提供一个不需要peewee的示例,否则我将无法测试它,因为在我当前的设置中,我无法安装它。我已经重新编写了代码,使其独立于peewee模块。请参见前面发布的代码示例: