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

QAbstractItemModel TreeView复选框不可选,setData未触发求助

问题修复方案

下面是导致TreeView复选框无法选中、setData不被调用的核心问题及修复步骤:

1. 代理模型需启用复选交互权限

ProxyModel的flags方法未返回ItemIsUserCheckable,导致视图判定复选框不可编辑,不会触发setData方法:

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

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

        # 添加上复选交互权限
        return (
                QtCore.Qt.ItemFlag.ItemIsEnabled
                | QtCore.Qt.ItemFlag.ItemIsSelectable
                | QtCore.Qt.ItemFlag.ItemIsUserCheckable
        )

2. 修正数据模型的复选状态返回逻辑

Model的data方法中,CheckStateRole原本固定返回Checked,需改为读取存储的实际状态:

def data(self, index, role=QtCore.Qt.ItemDataRole.DisplayRole):
    row = index.row()
    col = index.column()

    if not index.isValid():
        return None

    item = index.internalPointer()

    if role == QtCore.Qt.ItemDataRole.CheckStateRole and col == 0 and item.is_checkable:
        # 调用checkState获取存储的状态
        return self.checkState(QPersistentModelIndex(index))

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

3. setData方法添加数据变更信号

修改选中状态后必须发出dataChanged信号,通知视图刷新显示:

def setData(self, index, value, role=QtCore.Qt.ItemDataRole.EditRole):
    if not index.isValid():
        return False
    if role == QtCore.Qt.ItemDataRole.CheckStateRole:
        self.checks[QPersistentModelIndex(index)] = value
        # 发出信号通知视图更新
        self.dataChanged.emit(index, index, [role])
        return True
    return False

4. 数据添加时通知模型更新

newData方法直接修改子项列表但未通知模型,需用beginInsertRows和endInsertRows包裹操作:

def newData(self):
    items = ["foo", "bar", "baz"]
    parent_index = self.index(0, 0, QtCore.QModelIndex())
    for x in items:
        child = DBObject(x + "0", self._root)
        row = len(self._root.children)
        self.beginInsertRows(parent_index, row, row)
        self._root.children.append(child)
        self.endInsertRows()
        
        child_index = self.index(row, 0, parent_index)
        for y in items:
            grand_child = DBObject(y + "1", child, None, True)
            grand_row = len(child.children)
            self.beginInsertRows(child_index, grand_row, grand_row)
            child.children.append(grand_child)
            self.endInsertRows()

5. 修复类定义语法错误

MainWindow类定义末尾多了一个括号,修正为:

class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        # ... 原有代码 ...

完整修复后的代码

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

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

    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)
        self.newData()
        self.checks = {}

    def checkState(self, index):
        if index in self.checks.keys():
            return self.checks[index]
        else:
            return QtCore.Qt.CheckState.Unchecked

    def newData(self):
        items = ["foo", "bar", "baz"]
        parent_index = self.index(0, 0, QtCore.QModelIndex())
        for x in items:
            child = DBObject(x + "0", self._root)
            row = len(self._root.children)
            self.beginInsertRows(parent_index, row, row)
            self._root.children.append(child)
            self.endInsertRows()
            
            child_index = self.index(row, 0, parent_index)
            for y in items:
                grand_child = DBObject(y + "1", child, None, True)
                grand_row = len(child.children)
                self.beginInsertRows(child_index, grand_row, grand_row)
                child.children.append(grand_child)
                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):
        row = index.row()
        col = index.column()

        if not index.isValid():
            return None

        item = index.internalPointer()

        if role == QtCore.Qt.ItemDataRole.CheckStateRole and col == 0 and item.is_checkable:
            return self.checkState(QPersistentModelIndex(index))

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

    def setData(self, index, value, role=QtCore.Qt.ItemDataRole.EditRole):
        if not index.isValid():
            return False
        if role == QtCore.Qt.ItemDataRole.CheckStateRole:
            self.checks[QPersistentModelIndex(index)] = value
            self.dataChanged.emit(index, index, [role])
            return True
        return False


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

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

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


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()

内容的提问来源于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 14:00:58