删除第一行时 selectionChanged() 的行为
Behavior of selectionChanged() on removing first row
请运行以下代码(我使用的是Qt 5.9):
QTableWidget* tableWidget = new QTableWidget(2, 2, nullptr);
tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
tableWidget->setSelectionMode(QAbstractItemView::SingleSelection);
connect(tableWidget->selectionModel(), &QItemSelectionModel::selectionChanged,
[&](const QItemSelection& selected, const QItemSelection& deselected)
{ qDebug() << "selected =" << selected << endl << "deselected =" << deselected; });
tableWidget->show();
QTimer::singleShot(10000, [=](){ tableWidget->removeRow(0); });
10 秒内,select 两行中的第一行。您将看到调试输出。它会告诉你第 0 行是你点击 selected 的。
然后,10 秒后,第 0 行自动删除。调试输出现在显示第 1 行已 selected,第 0 行已删除selected。
后者对我来说没有任何意义。删除第 0 行时,我希望 "new" 行 0 之后被 selected。此外,视觉上 selected 的行仍然是第 0 行,而第 1 行根本不存在了。
自定义模型和通用视图也会发生这种情况,并通过指向不存在的行使我的应用程序崩溃。
这是期望的行为吗?我的误会在哪里?
在 删除所选行之前 更改它是非常有意义的。做相反的事情可能会导致读取悬空数据,例如,如果 UI 在模型更改时刷新,但视图包含过时的索引。
考虑两次删除第0行:第二次很明显选择必须更改(在这种情况下取消选择)before 删除 table 中的最后一行以避免将无效索引作为所选行。
您可以使用以下修改示例来查看模型何时实际更新。
auto tableWidget = new QTableWidget(2, 2, nullptr);
tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
tableWidget->setSelectionMode(QAbstractItemView::SingleSelection);
connect(tableWidget->selectionModel(), &QItemSelectionModel::selectionChanged,
[&](const QItemSelection& selected, const QItemSelection& deselected)
{ qDebug() << "selected =" << selected << endl << "deselected =" << deselected; });
connect(tableWidget->model(), &QAbstractItemModel::rowsRemoved, [&](const QModelIndex &, int first, int last)
{ qDebug() << "first row removed =" << first << endl << "last row removed =" << last; });
tableWidget->show();
QTimer::singleShot(10000, [=](){ tableWidget->removeRow(0); });
QTimer::singleShot(15000, [=](){ tableWidget->removeRow(0); }); // remove twice
解决方法
主要问题是,正如您在评论中指出的那样,您不能依赖信号信息:如果执行删除,那些 QModelIndex
可能有效也可能无效。您可以跟踪所有更改,但这会让人筋疲力尽。
相反,您可以尝试延迟 选择信号,这样当它被处理时,模型已经更新,您可以信任来自选择模型的信息。 技巧是使用定时器:处理超时事件的函数将在事件循环的下一次迭代中执行(即使超时时间为 0),而模型和小部件在当前迭代中更新 :
connect(tableWidget->selectionModel(), &QItemSelectionModel::selectionChanged,
[&](const QItemSelection&, const QItemSelection&) {
QTimer::singleShot(0, [&]() {
qDebug() << "selected =" << tableWidget->selectionModel()->selectedIndexes() << endl;
});
});
请运行以下代码(我使用的是Qt 5.9):
QTableWidget* tableWidget = new QTableWidget(2, 2, nullptr);
tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
tableWidget->setSelectionMode(QAbstractItemView::SingleSelection);
connect(tableWidget->selectionModel(), &QItemSelectionModel::selectionChanged,
[&](const QItemSelection& selected, const QItemSelection& deselected)
{ qDebug() << "selected =" << selected << endl << "deselected =" << deselected; });
tableWidget->show();
QTimer::singleShot(10000, [=](){ tableWidget->removeRow(0); });
10 秒内,select 两行中的第一行。您将看到调试输出。它会告诉你第 0 行是你点击 selected 的。 然后,10 秒后,第 0 行自动删除。调试输出现在显示第 1 行已 selected,第 0 行已删除selected。
后者对我来说没有任何意义。删除第 0 行时,我希望 "new" 行 0 之后被 selected。此外,视觉上 selected 的行仍然是第 0 行,而第 1 行根本不存在了。
自定义模型和通用视图也会发生这种情况,并通过指向不存在的行使我的应用程序崩溃。
这是期望的行为吗?我的误会在哪里?
在 删除所选行之前 更改它是非常有意义的。做相反的事情可能会导致读取悬空数据,例如,如果 UI 在模型更改时刷新,但视图包含过时的索引。
考虑两次删除第0行:第二次很明显选择必须更改(在这种情况下取消选择)before 删除 table 中的最后一行以避免将无效索引作为所选行。
您可以使用以下修改示例来查看模型何时实际更新。
auto tableWidget = new QTableWidget(2, 2, nullptr);
tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
tableWidget->setSelectionMode(QAbstractItemView::SingleSelection);
connect(tableWidget->selectionModel(), &QItemSelectionModel::selectionChanged,
[&](const QItemSelection& selected, const QItemSelection& deselected)
{ qDebug() << "selected =" << selected << endl << "deselected =" << deselected; });
connect(tableWidget->model(), &QAbstractItemModel::rowsRemoved, [&](const QModelIndex &, int first, int last)
{ qDebug() << "first row removed =" << first << endl << "last row removed =" << last; });
tableWidget->show();
QTimer::singleShot(10000, [=](){ tableWidget->removeRow(0); });
QTimer::singleShot(15000, [=](){ tableWidget->removeRow(0); }); // remove twice
解决方法
主要问题是,正如您在评论中指出的那样,您不能依赖信号信息:如果执行删除,那些 QModelIndex
可能有效也可能无效。您可以跟踪所有更改,但这会让人筋疲力尽。
相反,您可以尝试延迟 选择信号,这样当它被处理时,模型已经更新,您可以信任来自选择模型的信息。 技巧是使用定时器:处理超时事件的函数将在事件循环的下一次迭代中执行(即使超时时间为 0),而模型和小部件在当前迭代中更新 :
connect(tableWidget->selectionModel(), &QItemSelectionModel::selectionChanged,
[&](const QItemSelection&, const QItemSelection&) {
QTimer::singleShot(0, [&]() {
qDebug() << "selected =" << tableWidget->selectionModel()->selectedIndexes() << endl;
});
});