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
相关产品推荐
相关产品推荐

