You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用QSortFilterProxyModel后QTreeView插入数据不更新的问题

问题分析与解决

一、QAbstractItemModel与QSortFilterProxyModel的交互机制

QSortFilterProxyModel是数据代理中间层,本身不存储业务数据,完全依赖绑定的源模型(QAbstractItemModel)提供数据。它的核心交互逻辑是:

  • 绑定源模型后,代理模型会监听源模型发出的所有数据变更信号(如rowsInserted、dataChanged等)。
  • 源模型修改数据时,必须主动发射对应信号,代理模型才能感知到变化,进而更新自身过滤结果,最终通知视图(QTreeView)刷新显示。
  • 如果源模型修改数据后不发射信号,代理模型和视图无法感知数据变化,自然不会显示新内容。

二、除setSourceModel外的关键配置/实现要求

1. 源模型必须严格实现数据变更的信号发射

这是你当前问题的核心原因:你的Model类中newData()方法直接修改_root.children列表,但没有发射beginInsertRows和endInsertRows信号,导致代理模型完全不知道有新数据插入。

2. 代理模型的过滤规则配置(你已正确实现)

你已经设置了setFilterKeyColumn(0)和setRecursiveFilteringEnabled(True),确保了递归过滤第一列内容,这部分无需修改。

3. 源模型的index/parent方法正确实现(你已正确实现)

你的index()和parent()方法逻辑正确,能构建模型索引的父子关系,这是代理模型正确映射数据的基础。

三、修正后的代码

主要修改Model类的newData()方法,在插入子项前后发射数据变更信号:

import logging
import sys
from PySide6 import QtCore, QtWidgets
from PySide6.QtCore import QSortFilterProxyModel

class DBObject:
    def __init__(self, name, parent, children=None):
        self.name = name
        self.parent = parent
        self.children = children or list()

    def __repr__(self):
        return f"name: {self.name}, parent: {self.parent.name if self.parent is not None else '-'}"


class Model(QtCore.QAbstractItemModel):
    def __init__(self, parent=None):
        super().__init__(parent)
        self._root = DBObject("root", None)

    def newData(self):
        items = ["foo", "bar", "baz"]
        # 获取根节点的模型索引
        root_index = self.index(0, 0, QtCore.QModelIndex())
        
        # 插入一级子项(x+0)
        start_row = len(self._root.children)
        end_row = start_row + len(items) - 1
        self.beginInsertRows(root_index, start_row, end_row)
        
        for x in items:
            child = DBObject(x + "0", self._root)
            self._root.children.append(child)
            
            # 插入二级子项(y+1)
            child_index = self.index(len(self._root.children)-1, 0, root_index)
            child_start_row = len(child.children)
            child_end_row = child_start_row + len(items) - 1
            self.beginInsertRows(child_index, child_start_row, child_end_row)
            
            for y in items:
                child.children.append(DBObject(y + "1", child))
            
            self.endInsertRows()
        
        self.endInsertRows()

    def columnCount(self, parent=QtCore.QModelIndex()):
        return 1

    def rowCount(self, parent=QtCore.QModelIndex()):
        if not parent.isValid():
            return 1

        parentItem = parent.internalPointer()
        rowCount = len(parentItem.children)
        logging.info(f"rowCount({parentItem}): rowCount={rowCount}")
        return rowCount

    def parent(self, index):
        if not index.isValid():
            return QtCore.QModelIndex()

        item = index.internalPointer()
        parentItem = item.parent

        logging.info(f"parent({item}): parent={parentItem}")
        if parentItem is None:
            return QtCore.QModelIndex()
        else:
            if parentItem.parent is None:
                return self.createIndex(0, 0, parentItem)
            else:
                return self.createIndex(parentItem.parent.children.index(parentItem), 0, parentItem)

    def index(self, row, column, parent=QtCore.QModelIndex()):
        if not parent.isValid():
            if row != 0 or column != 0:
                return QtCore.QModelIndex()
            else:
                logging.info(f"index({row}, {column}, None): index={self._root}")
                return self.createIndex(0, 0, self._root)

        parentItem = parent.internalPointer()

        if 0 <= row < len(parentItem.children):
            logging.info(f"index({row}, {column}, {parentItem}): index={parentItem.children[row]}")
            return self.createIndex(row, column, parentItem.children[row])
        else:
            logging.info(f"index({row}, {column}, {parentItem}): index=None")
            return QtCore.QModelIndex()

    def data(self, index, role=QtCore.Qt.ItemDataRole.DisplayRole):
        if not index.isValid():
            return None

        item = index.internalPointer()

        if role == QtCore.Qt.ItemDataRole.DisplayRole:
            return item.name
        else:
            return None

    def flags(self, index):
        if not index.isValid():
            return QtCore.Qt.ItemFlag.NoItemFlags

        return (
            QtCore.Qt.ItemFlag.ItemIsEnabled
            | QtCore.Qt.ItemFlag.ItemIsSelectable)


class ProxyModel(QSortFilterProxyModel):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setFilterKeyColumn(0)
        self.setRecursiveFilteringEnabled(True)


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.setMinimumSize(640, 480)

        centralWidget = QtWidgets.QWidget(self)
        self.setCentralWidget(centralWidget)

        layout = QtWidgets.QVBoxLayout(centralWidget)

        self._treeView = QtWidgets.QTreeView(self)
        layout.addWidget(self._treeView)

        self._model = Model()
        self._proxyModel = ProxyModel()
        self._proxyModel.setSourceModel(self._model)
        self._treeView.setModel(self._proxyModel)

        self._proxyModel.setFilterFixedString("bar1")

        button = QtWidgets.QPushButton("Add")
        layout.addWidget(button)

        button.clicked.connect(self._Clicked)

    def _Clicked(self):
        self._model.newData()
        self._treeView.expandAll()

def main():
    app = QtWidgets.QApplication(sys.argv)

    mainWindow = MainWindow()
    mainWindow.show()
    app.exec()

if __name__ == "__main__":
    main()

四、关键修改说明

在newData()中,插入每一层子项时都遵循以下流程:

  1. 调用beginInsertRows(parent_index, start_row, end_row):通知模型和代理,从start_row到end_row的行将被插入到parent_index对应的父项之下。
  2. 执行实际的数据插入操作(修改children列表)。
  3. 调用endInsertRows():通知插入完成,触发模型更新信号。

这样代理模型就能接收到rowsInserted信号,更新过滤后的数据集,视图也会自动刷新显示新内容。

内容的提问来源于stack exchange,提问作者lebhero

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 22:50:20