Из обработчика сигнала dataChanged модели данных нельзя вызывать setData.
Надо понимать, что dataChanged испускается в модели данных как раз в функции setData и таким образом вызовы setData получатся вложенными (возникнет рекурсия), а этого лучше избегать.
И хотя испускание dataChanged происходит в самом конце setData все-таки лучше в обработчике dataChanged не использовать setData.
bool QSqlTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
Q_D(QSqlTableModel);
if (role != Qt::EditRole)
return QSqlQueryModel::setData(index, value, role);
if (!index.isValid() || index.column() >= d->rec.count() || index.row() >= rowCount())
return false;
bool isOk = true;
switch (d->strategy) {
case OnFieldChange: {
if (index.row() == d->insertIndex)
{
// это признак , что мы в режиме вставки новой строки
QSqlTableModelPrivate::setGeneratedValue(d->editBuffer, index.column(), value);
// пока ничего не пишется в базу данных, накапливаем в буфере данные по полям
return true;
}
// значит мы в режиме редактирования строки
d->clearEditBuffer(); // потому что при OnFieldChange изменение только одного поля пишется в базу, но вдруг еще какие-то поля в буфере установлены (generated yes)
// также происходит инициализация editBuffer - это важно
// устанавливаем значение поля generated=yes
QSqlTableModelPrivate::setGeneratedValue(d->editBuffer, index.column(), value);
// поскольку только одно поле помечено будет как generated
// то updateRowInTable только одно поле изменит в базе.
isOk = updateRowInTable(index.row(), d->editBuffer);
if (isOk)
select(); // поскольку у нас OnFieldChange обновляем талицу заново
emit dataChanged(index, index); // сигнализируем, что поле в базе уже изменено
break;
}
case OnRowChange:
if (index.row() == d->insertIndex)
{
// это признак , что мы в режиме вставки новой строки
// (несуществующей еще в базе данных строки)
QSqlTableModelPrivate::setGeneratedValue(d->editBuffer, index.column(), value);
return true;
}
if (d->editIndex != index.row())
{
// если текущая строка почему-то не в режиме редактирования данной текущей строки
if (d->editIndex != -1) // но в режиме редактирования почему-то другой строки
submit(); // то надо записать эту другую строку сначала в базу данных
d->clearEditBuffer(); // и очистить editBuffer для текущей строки
}
qDebug() << "56858637856965 " <editBuffer;
// устанавливаем значение поля в editBuffer
QSqlTableModelPrivate::setGeneratedValue(d->editBuffer, index.column(), value);
// выставляем в editIndex, что мы редактируем это строку
d->editIndex = index.row();
emit dataChanged(index, index); // сигнализируем, что поле изменено
// но еще не записано в базу данных (в отличии от OnFieldChange)
break;
case OnManualSubmit:
{
// читаем из кеша QMap текущую строку
QSqlTableModelPrivate::ModifiedRow &row = d->cache[index.row()];
// тут важно, что если в кеше нет еще такой row
// то при использовании для доступа через квадратные скобки []
// новый пустой ModifiedRow будет создан !
if (row.op == QSqlTableModelPrivate::None)
{
// в кеше не было такой row , создали новый элемент ModifiedRow
row.op = QSqlTableModelPrivate::Update;
// запоминаем данные всех полей текущей строки в rec
row.rec = d->rec;
// на всякий случай очищаем поля для новой строки кеша
QSqlTableModelPrivate::clearGenerated(row.rec);
// QSqlRecord primaryValues заполнятеся данными из query
// QSqlQueryModel::indexInQuery - создает индекс в базе данных, если его нет еще
// primaryValues тип QSqlRecord, но там только индексные поля содержатся, вот пример:
// SqlRecord( 1 ) " 0:" QSqlField("id", int, required: no, generated: yes) "39"
row.primaryValues = d->primaryValues(indexInQuery(index).row());
//qDebug() << " row.primaryValues " << row.primaryValues;
}
// устанавливаем значение поля в row.rec (строка кеша)
QSqlTableModelPrivate::setGeneratedValue(row.rec, index.column(), value);
emit dataChanged(index, index); // сигнализируем об изменении поля строки
// это еще только в кеше, в самой базе данных изменений еще нет
break;
}
}
return isOk;
}
Может показаться, что отрабатывать изменения поля в одной ячейки строки, чтобы потом изменить значение в другой ячейки строки можно переопределив QAbstractItemView::commitData (класс представления данных). И это будет правильно с той точки зрения, что первый вызов setData уже закончился и второй вызов setData не прервет первый вызов.