Details
-
Bug
-
Resolution: Out of scope
-
P2: Important
-
4.2.2
-
None
Description
QSqlTableModel::setData() will call select() if the editStrategy is OnFieldChange. This will in turn remove all rows from the model and thus invalidate all persistent model indexes.
This leads to unexpected behavior with for example QDataWidgetMapper:
See example:
#include <QVBoxLayout> #include <QMainWindow> #include <QSqlDatabase> #include <QMessageBox> #include <QSqlQuery> #include <QWidget> #include <QApplication> #include <QTableView> #include <QSqlTableModel> #include <QLineEdit> #include <QLabel> #include <QHBoxLayout> #include <QDebug> #include <QAbstractItemView> #include <QModelIndex> #include <QDataWidgetMapper> #include <QShortcut> #include <QKeySequence> #include <QTextEdit> #include <QHeaderView> static bool createConnection() { QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE"); db.setDatabaseName(":memory:"); if (!db.open()) { QMessageBox::critical(0, qApp->tr("Cannot open database"), qApp->tr("Unable to establish a database connection.\n" "This example needs SQLite support. Please read " "the Qt SQL driver documentation for information how " "to build it.\n\n" "Click Cancel to exit."), QMessageBox::Cancel); return false; } QSqlQuery query; query.exec("create table person(" "firstname varchar(1024), lastname varchar(1024))"); query.exec("insert into person values('Danny', 'Young')"); query.exec("insert into person values('Christine', 'Holand')"); query.exec("insert into person values('Lars', 'Gordon')"); query.exec("insert into person values('Roberto', 'Robitaille')"); query.exec("insert into person values('Maria', 'Papadopoulos')"); return true; } class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = 0) : QMainWindow(parent) { setCentralWidget(new QWidget(this)); QVBoxLayout *lay = new QVBoxLayout(centralWidget()); QHBoxLayout *hbox = new QHBoxLayout; QLabel *lbl = new QLabel("&firstName", centralWidget()); hbox->addWidget(lbl); QLineEdit *firstName = new QLineEdit(centralWidget()); hbox->addWidget(firstName); lbl->setBuddy(firstName); lay->addLayout(hbox); hbox = new QHBoxLayout; lbl = new QLabel("&lastName", centralWidget()); hbox->addWidget(lbl); QLineEdit *lastName = new QLineEdit(centralWidget()); hbox->addWidget(lastName); lbl->setBuddy(lastName); lay->addLayout(hbox); QSqlTableModel *model = new QSqlTableModel(this); #if 1 model->setEditStrategy(QSqlTableModel::OnFieldChange); #else model->setEditStrategy(QSqlTableModel::OnManualSubmit); #endif model->setTable("person"); model->select(); QTableView *view = new QTableView(centralWidget()); view->setEditTriggers(QAbstractItemView::NoEditTriggers); view->setModel(model); view->horizontalHeader()->setStretchLastSection(true); connect(view->selectionModel(), SIGNAL(currentRowChanged(QModelIndex, QModelIndex)), this, SLOT(onSelected(QModelIndex))); lay->addWidget(view); mapper.setSubmitPolicy(QDataWidgetMapper::ManualSubmit); mapper.setModel(model); mapper.addMapping(firstName, 0); mapper.addMapping(lastName, 1); new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_S), this, SLOT(save())); view->setSelectionBehavior(QAbstractItemView::SelectRows); view->setSelectionMode(QAbstractItemView::SingleSelection); view->selectRow(0); } public slots: void save() { mapper.submit(); qDebug() << "Submit failed"; } void onSelected(const QModelIndex &index) { mapper.setCurrentModelIndex(index); } private: QDataWidgetMapper mapper; }; #include "main.moc" int main(int argc, char **argv) { QApplication a(argc, argv); if (!createConnection()) return 1; MainWindow w; w.show(); return a.exec(); }
To reproduce:
run example.
Type something in the lastName lineedit and type CTRL+S. The submit operation will fail.
Change the EditStrategy to OnManualSubmit and it works like expected.
The reason it fails is this:
bool QDataWidgetMapper::submit() { Q_D(QDataWidgetMapper); for (int i = 0; i < d->widgetMap.count(); ++i) { const QDataWidgetMapperPrivate::WidgetMapper &m = d->widgetMap.at(i); if (m.widget.isNull()) continue; if (!m.currentIndex.isValid()) return false; d->delegate->setModelData(m.widget, d->model, m.currentIndex); } return d->model->submit(); }
This one will fail because the select() that is called from QSqlTableMode::setData() will effectively invalidate all persistent model indexes.
if (!m.currentIndex.isValid()) <-- return false;