PySide6中TreeView与TableView特定交互序列致崩溃求助
问题背景
使用Python 3.11 + PySide6开发的项目中,包含以下组件:
- treeView(基于QStandardItemModel)
- tableView1(基于QAbstractTableModel)
- tableView2(基于QAbstractTableModel + QSortFilterProxyModel)
基础功能正常:选择treeView项后,tableView1展示选中对象属性,tableView2展示匹配的SQL数据库条目并支持编辑。但按以下操作序列会触发无提示崩溃(线程直接终止,调试器无响应):
- 点击treeView项,tableView1/2完成更新
- 点击tableView2列头(已开启
setSortingEnabled(True))触发排序 - 点击tableView2任意单元格仅高亮
- 点击treeView另一项,tableView1/2更新
- 再次点击tableView2列头 → 程序崩溃
此前通过清除tableView2的selectionModel解决过类似崩溃,但该方法对当前场景无效;禁用QSortFilterProxyModel后崩溃消失。
解决方案
崩溃根源是切换treeView项更新tableView2模型时,QSortFilterProxyModel的排序状态未完全重置,导致后续排序操作访问无效内存。需在模型切换时完整清理proxy和视图的关联状态,具体步骤如下:
1. 重置QSortFilterProxyModel的关联与排序状态
在treeView选中项变更的处理函数中,先断开proxy与旧模型的关联,清除排序状态后再绑定新模型:
from PySide6.QtCore import Qt def on_tree_selection_changed(self, selected, deselected): # 获取选中项对应的新tableView2数据模型 new_source_model = self._get_table2_model(selected.indexes()[0]) # 1. 断开proxy与旧模型的关联 self.proxy_model.setSourceModel(None) # 2. 清除proxy的排序状态(-1表示取消排序) self.proxy_model.sort(-1) # 3. 清除tableView2列头的排序指示器 self.tableView2.horizontalHeader().setSortIndicator(-1, Qt.AscendingOrder) # 4. 绑定新的源模型 self.proxy_model.setSourceModel(new_source_model)
2. 彻底清理tableView2的选择与焦点
除了清除selectionModel,还要确保视图释放对旧模型索引的引用:
# 清除选择并释放焦点 self.tableView2.selectionModel().clearSelection() self.tableView2.clearFocus() # 可选:临时关闭排序再重新开启,确保视图状态同步 self.tableView2.setSortingEnabled(False) self.tableView2.setSortingEnabled(True)
3. 确保SQL模型加载的同步性
如果tableView2的源模型是从SQL异步加载数据,需等待数据加载完成后再更新proxy模型,避免proxy绑定未初始化完成的模型:
# 示例:等待SQL模型加载完成的信号 self.sql_model.load_finished.connect(lambda: self._update_table2_proxy(new_source_model))
原理说明
此前仅清除selectionModel的操作,未处理QSortFilterProxyModel内部保存的排序列、排序方向等状态——这些状态是和旧源模型的列结构绑定的。当切换新模型后,proxy的排序状态指向旧模型的无效索引,再次点击列头触发排序时,会访问已释放的内存空间,导致程序无提示崩溃。通过先断开模型关联、重置排序状态,可彻底避免这类无效引用问题。
内容的提问来源于stack exchange,提问作者Vailsen Pic

