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
    创建新记录
  • =>结果:Python陷入无休止的循环,直到出现上述错误消息

    =>预期结果:应该创建行,而不是被筛选器命中。删除筛选器时,应显示所有行,以及新创建的行

    完整工作代码示例:

    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()
    的调用columnCount需要当前代理模型的映射,而当前代理模型又会再次调用filterAcceptsRow,从而使函数递归


    如果为0,则使用
    ,就是这样!精彩的!非常感谢你!你现在知道我花了多少时间在这上面了!也许你在这里也有一个想法@ProfP30不客气,是的,我知道在您完全理解模型的工作原理之前,跟踪这些问题可能有多困难(提示:在您不理解Qt正在做什么的时候查找)。关于另一个问题,我很抱歉,除非您能够提供一个不需要peewee的示例,否则我将无法测试它,因为在我当前的设置中,我无法安装它。我已经重新编写了代码,使其独立于peewee模块。请参见前面发布的代码示例: