Пожалуйста, запустите следующий код (я использую 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 секунд выберите первый из двух рядов. Вы увидите отладочный вывод. Он покажет вам, что строка 0 была выбрана по вашему щелчку.
Затем, через 10 секунд, строка 0 удаляется автоматически. Вывод отладочной информации теперь показывает, что строка 1 выбрана, а строка 0 отменена.
Последнее не имеет никакого смысла для меня. При удалении строки 0 я ожидал, что «новая» строка 0 будет выбрана впоследствии. Также визуально выбранная строка по-прежнему является строкой 0, а строка 1 просто больше не существует.
Это также происходит с пользовательской моделью и общим представлением и приводит к сбою приложения, указывая на несуществующую строку.
Это желаемое поведение? Где мое недоразумение?
Имеет смысл изменить выбранную строку до удаляя его. Выполнение противоположного действия может привести к чтению висячих данных, например, если пользовательский интерфейс обновляется после изменения модели, но представление содержит устаревшие индексы.
Подумайте об удалении строки 0 дважды: во второй раз очень очевидно, что выделение должен быть изменено (отменено в этом случае) до удаление последней строки в таблице, чтобы избежать использования неверного индекса в качестве выбранной строки.
Вы можете использовать следующий модифицированный пример, чтобы увидеть, когда модель фактически обновляется.
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;
});
});
Других решений пока нет …