0

I have a very large file with simple structured XML data (about 180,000 records). I need a dialog that will show a progress bar and parse the data to the database using another thread. One of the difficulties is that the database to which I need to write data is already in use by the main window.

What app does:

  1. MainWindow opens database and works with it.
  2. If action "Parse and write" is clicked then mainwindow: (our dialogue)
void MainWindow::on_act_parse_and_write()
{
    // CLOSE DB
    db->close();
    delete db;
    
    // EXEC DLG
    update_omega_base * dlg = new update_omega_base(this);
    dlg->exec();

    // OPEN NEW CONNECTION
    db = new QSqlDatabase();
    *db = QSqlDatabase::addDatabase("QSQLITE");
    db->setDatabaseName(DB_NAME);
    if (!db->open()) {
        QMessageBox::critical(nullptr, "Помилка", "Не можу підключити базу даних MAINWINDOW", QMessageBox::Ok);
        exit(ERROR_CODE);
    }
}
  1. My dlg creates a gui:

dlg_parse_and_write

  1. If left button clicked then program creates a new thread and edits progressBar:
    parse_and_writeToDB *worker = new parse_and_writeToDB();
    worker->moveToThread(&workThread);

    connect(&workThread, &QThread::finished, &workThread, &QObject::deleteLater);
    connect(this, &update_omega_base::startWork, worker, &parse_and_writeToDB::doWork);
    connect(worker, &parse_and_writeToDB::currentRowChanged, this, &update_omega_base::updateCurrentProgress);
    connect(worker, &parse_and_writeToDB::errorDetected, this, &update_omega_base::handleError);

    workThread.start();
  1. doWork() in "parse_and_writeToDB" class:
    // CREATE CONNECTION TO DATABASE
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
    db.setDatabaseName(DB_NAME);
    if (!db.open()) {
        emit errorDetected("Не можу відкрити базу даних");
        exit(ERROR_CODE);
    }

    QSqlQuery qry;

    // clear old Omega table
    if(!qry.exec("DELETE FROM " + DB_TABLE_OMEGA))
    {
        emit errorDetected("Не виходить очистити БД перед записом");
        exit(ERROR_CODE);
    }

    // ------- QXmlStreamParser PARSES EVERYTHING and qry ADDS ROWS -------

    qry.clear();
    db.close();
  1. After working for a while, the application crashes. Output:
QSqlDatabasePrivate::addDatabase: duplicate connection name 'qt_sql_default_connection', old connection removed.
QObject::~QObject: Timers cannot be stopped from another thread

UPDATE #1 - invodeMethod - is too slow and the work anyway is done using main thread. During writing to DB gui feels bad.

SageCat
  • 315
  • 1
  • 13
  • So your real question is "How do I access db from multiple threads", right? Try renaming your connection, as explained [here](https://stackoverflow.com/questions/47457478/using-qsqlquery-from-multiple-threads). – JarMan Jan 07 '21 at 14:10
  • @JarMan No, my real question is "Why am I having such errors? I thought I did everything right." – SageCat Jan 08 '21 at 08:57

1 Answers1

0

You can call metaCall from different thread. Like this:

QMetaObject::invokeMethod(obj, "slot",
                            Qt::QueuedConnection);

obj is the QObject pointer, "slot" is the function you define as "public SLOTS"

  • do you check the return value of the invokeMethod ? I mean bool bRet = QMetaObject::invokeMethod(parent, "addRowToDB", Qt::QueuedConnection, ); if bRet = true ,than the method will execute. otherwise check the arguments. – Derek Zheng Jan 11 '21 at 01:33
  • Yeah, everything with the invokeMethod is fine. There are 2 problems: 1) the error i got during parsing 90+ lines of my xml-file. 2) It's too slow. The work with db anyway is done using main thread and that's why gui feels bad. Now, i see that i need to find path to write DB using another thread (the solution with creating new DB per thread is bad for me). Anyway, thank you) – SageCat Jan 12 '21 at 08:55