Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. [SOLVED] Updating QSqlRecord During QSqlTableModel::beforeUpdate Causes Blank Row In QTableView
Forum Updated to NodeBB v4.3 + New Features

[SOLVED] Updating QSqlRecord During QSqlTableModel::beforeUpdate Causes Blank Row In QTableView

Scheduled Pinned Locked Moved General and Desktop
4 Posts 1 Posters 5.4k Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • B Offline
    B Offline
    Beacon11
    wrote on last edited by
    #1

    Hey all.

    I have a Qt5 program that utilizes an Sqlite3 database hooked up to a QSqlTableModel, which is handed to a QTableView. The model is editable via the QTableView with the default submission options (I have to leave focus before changes are committed). However, I needed to intercept changes made in the QTableView and possibly change them, so I created a slot connected to the QSqlTableModel's beforeUpdate(int,QSqlRecord&) signal.

    @
    QSqlDatabase database = QSqlDatabase::addDatabase("QSQLITE");
    database.setDatabaseName("test.sqlite3");
    database.open();

    QSqlQuery query;
    query.exec("CREATE TABLE example (testing text)");

    QSqlTableModel *sqlTableModel = new QSqlTableModel(this);
    sqlTableModel->setTable("example");
    sqlTableModel->select();
    sqlTableModel->setHeaderData(0, Qt::Horizontal, tr("Testing"));

    connect(sqlTableModel, SIGNAL(beforeUpdate(int,QSqlRecord&)), SLOT(handleBeforeUpdate(int,QSqlRecord&)));

    // QTableView
    m_ui->tableView->setModel(sqlTableModel);
    @

    And the slot:

    @
    void MainWindow::handleBeforeUpdate(int row, QSqlRecord &record)
    {
    Q_UNUSED(row);

    record.setValue("testing", "foo");
    record.setGenerated("testing", true);
    

    }
    @

    Now, to see this for yourself: Insert something into that table. Then double-click it in the QTableView. Edit it, and what I EXPECT to see is instead of that cell in the database being set to whatever you wrote, it's set to "foo."

    In fact, that IS what happens to the database. However, the QTableView doesn't show that-- it shows a blank row that I can select but do nothing with. If I close the program and reopen it, it shows exactly what I want.

    Why isn't this working? Since the record is pass-by-reference through the signal, shouldn't it? If not, is there a standard way to intercept edits on the model before it gets to the database without the view getting all screwed up?

    I would appreciate any advice!

    1 Reply Last reply
    0
    • B Offline
      B Offline
      Beacon11
      wrote on last edited by
      #2

      Note that if handleBeforeUpdate changes the record's value of "testing" to the same value that was set in the GUI (in this example, if the value set in the GUI was "foo"), it works fine. There's something wonky that happens when the record is updated with a new value-- not just simply updated period. Also, note that if one were to change the code to this:

      @
      void MainWindow::handleBeforeUpdate(int row, QSqlRecord &record)
      {
      Q_UNUSED(row);

      qDebug() << "Before:" << record;
      record.setValue("testing", "foo");
      record.setGenerated("testing", true);
      qDebug() << "After:" << record;
      

      }
      @

      The record printed out before and after are literally identical other than the changed string.

      1 Reply Last reply
      0
      • B Offline
        B Offline
        Beacon11
        wrote on last edited by
        #3

        Alright, I've written up the smallest, simplest working example that I can. Please test this and tell me what's going on:

        main.cpp:

        @
        #include <QApplication>

        #include "mainwindow.h"

        int main(int argc, char *argv[])
        {
        QApplication a(argc, argv);
        MainWindow w;
        w.show();

        return a.exec&#40;&#41;;
        

        }
        @

        mainwindow.h:

        @
        #ifndef MAINWINDOW_H
        #define MAINWINDOW_H

        #include <QSqlRecord>
        #include <QMainWindow>

        class QSqlTableModel;

        class MainWindow : public QMainWindow
        {
        Q_OBJECT

        public:
            explicit MainWindow(QWidget *parent = 0);
        
        private slots:
            void handleBeforeUpdate(int row, QSqlRecord &record);
        
        private:
            QSqlTableModel *m_model;
        

        };

        #endif // MAINWINDOW_H
        @

        mainwindow.cpp:

        @
        #include <QTableView>
        #include <QSqlTableModel>
        #include <QMessageBox>
        #include <QSqlQuery>
        #include <QDebug>

        #include "mainwindow.h"

        MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent)
        {
        QSqlDatabase database = QSqlDatabase::addDatabase("QSQLITE");
        database.setDatabaseName("example.sqlite3");
        if (!database.open())
        {
        QMessageBox::critical(this, tr("Cannot open database"),
        tr("Unable to establish a database connection.\n"
        "This program needs SQLite support.\n"
        "Click Cancel to exit."), QMessageBox::Cancel);
        close();
        }

        QSqlQuery query;
        if (!query.exec&#40;"CREATE TABLE IF NOT EXISTS example (testing text&#41;"))
        {
            QMessageBox::critical(this, tr("Cannot create table"),
                                  tr("Click Cancel to exit."), QMessageBox::Cancel);
        }
        
        query.exec&#40;"INSERT INTO example (testing&#41; VALUES ('testing')");
        
        m_model = new QSqlTableModel(this);
        m_model->setTable("example");
        m_model->select();
        
        connect(m_model, SIGNAL(beforeUpdate(int,QSqlRecord&)), SLOT(handleBeforeUpdate(int,QSqlRecord&)));
        
        QTableView *tableView = new QTableView(this);
        tableView->setModel(m_model);
        setCentralWidget(tableView);
        

        }

        void MainWindow::handleBeforeUpdate(int row, QSqlRecord &record)
        {
        qDebug() << "Before: " << record;
        record.setValue("testing", "intercepted");
        record.setGenerated("testing", true);
        qDebug() << "After: " << record;

        // Before and after should be identical other than the changed strings.
        // Why doesn't this work?? Try changing the string in the TableView to
        // "intercepted" (without quotes), and note that it works just fine.
        

        }
        @

        1 Reply Last reply
        0
        • B Offline
          B Offline
          Beacon11
          wrote on last edited by
          #4

          I figured it out. I learned two things:

          1. beforeUpdate() is before the DATABASE is updated, not before the CACHE is updated.
          2. Turns out that the cache can easily get out of sync with the database if it's lacking a serial ID.

          The fix to all my problems (and another bug in the example I posted) is to simply add an "id integer primary key" to the database table. Then all of a sudden it works as expected.

          1 Reply Last reply
          1

          • Login

          • Login or register to search.
          • First post
            Last post
          0
          • Categories
          • Recent
          • Tags
          • Popular
          • Users
          • Groups
          • Search
          • Get Qt Extensions
          • Unsolved