Qt4转Qt5后QModelIndex设置引发段错误,如何解决?
问题分析与解决方案
这个坑我帮好几个从Qt4迁Qt5的开发者踩过,核心问题出在Qt5中QSqlQueryModel::setQuery()的行为变更,直接导致你保存的QModelIndex变成了无效指针,触发段错误。
QModelIndex在Qt5中的关键变更
在Qt4里,QSqlQueryModel::setQuery()执行后,模型可能只是更新数据内容,不会完全重置模型的内部结构——旧的QModelIndex虽然指向的是旧数据,但内存还没被销毁,所以调用setCurrentIndex(idx)不会立刻崩溃(其实这是个不规范的行为)。
但Qt5严格遵循了MVC框架的规范:当调用setQuery()时,QSqlQueryModel会触发modelReset()信号,彻底销毁旧的模型数据结构,所有之前获取的QModelIndex都会变成无效索引(指向的内存已经被释放)。这时候再用这个无效索引去调用setCurrentIndex(),自然会触发段错误。
避免错误的两种可行方案
方案1:保存行号/列号(简单场景)
如果你的数据排序、行数变化不大,直接保存当前选中的行号和列号,重置模型后重新生成有效的QModelIndex:
void MainWindow::on_timer() { // 保存当前选中的行和列,而不是QModelIndex int currentRow = ui->partyView->currentIndex().row(); int currentCol = ui->partyView->currentIndex().column(); partyModel.setQuery(partyModel.q_main + partyModel.q_where); // 检查行号是否有效(防止重置后数据行数减少,行号越界) if (currentRow >= 0 && currentRow < partyModel.rowCount()) { QModelIndex newIdx = partyModel.index(currentRow, currentCol); ui->partyView->setCurrentIndex(newIdx); } }
方案2:保存唯一标识(精准场景)
如果你的数据可能出现排序、行号变动的情况,建议保存每条记录的唯一标识(比如ID字段),重置模型后通过唯一标识定位到目标行:
// 假设你的模型中第0列是唯一ID列 const int ID_COLUMN = 0; void MainWindow::on_timer() { QModelIndex idx = ui->partyView->currentIndex(); QString currentRecordId; // 保存当前选中记录的唯一ID if (idx.isValid()) { currentRecordId = partyModel.data(partyModel.index(idx.row(), ID_COLUMN)).toString(); } partyModel.setQuery(partyModel.q_main + partyModel.q_where); // 根据ID查找并定位新的索引 if (!currentRecordId.isEmpty()) { for (int row = 0; row < partyModel.rowCount(); ++row) { QString id = partyModel.data(partyModel.index(row, ID_COLUMN)).toString(); if (id == currentRecordId) { ui->partyView->setCurrentIndex(partyModel.index(row, idx.column())); break; } } } }
额外提醒
其实在Qt4中直接复用旧QModelIndex的行为本身就不安全,只是没触发崩溃而已。Qt5的变更其实是让模型的行为更规范,符合MVC的设计原则——QModelIndex是和当前模型的状态强绑定的,一旦模型结构变化(重置、增删行等),旧索引就会失效,永远不要复用失效的索引。
内容的提问来源于stack exchange,提问作者Michael
相关产品推荐
相关产品推荐

