Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-431

QSqlTableModel::editStrategy, QDataWidgetMapper and persistent model indexes

    XMLWordPrintable

Details

    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; 
      

      Attachments

        No reviews matched the request. Check your Options in the drop-down menu of this sections header.

        Activity

          People

            bjnilsen Bjørn Erik Nilsen
            rve Anders Bakken
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved:

              Gerrit Reviews

                There are no open Gerrit changes