Alosyius
Alosyius

Reputation: 9111

QT GUI freezes even though Im running in separate thread

I have a small chat application where I use a SQLite database to store all the conversations. I've noticed that the app freezes randomly, and I then have to minimize and maximize it to make it work again. I thought that the problem might be the SQLite selects / inserts that were causing the gui to freeze. I decided to try and move all the SQLite methods into a separate thread.

After doing so the app still freezes.

Some things that might be worth knowing:

  1. I use QTcpSocket directly in my MainWindow but it seems that there is no use in running the QTcpSocket in a separate thread?

  2. I have separated the SQLite methods into a new thread (see implementation below)

  3. I use 3 WebViews for displaying my chat messages, the entire application GUI is build with these WebViews

Does my code below really run in a separate thread? GUI still freezes.

My header file:

class dbThread : public QObject
{
     Q_OBJECT

public:
     dbThread(QObject* parent);

public slots:
     bool openDB(QString agentID);

signals:
     void clearPreviousHistory();

private:
     QSqlDatabase db;
     QHash<QString, QString> countries;
};

My cpp file:

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
QThread* thread = new QThread(this);
dbtrad = new dbThread(this);
dbtrad->moveToThread(thread);

dbtrad->openDB(userID);

connect(dbtrad, SIGNAL(clearPreviousHistory()), this, SLOT(clearHistoryV()));
thread->start();

}



dbThread::dbThread(QObject * parent): QObject(parent) {
}

bool dbThread::openDB(QString agentID) {

    qDebug() << "OPEN DB FROM THREAD ";

    // Find QSLite driver
    db = QSqlDatabase::addDatabase("QSQLITE");

    // ......
}

This is how I call dbThread methods from my MainWindow:

dbtrad->getHistory(channelId);

Edit

New code:

// Start database thread
QThread* thread = new QThread(this);
dbtrad = new dbThread(this);
dbtrad->moveToThread(thread);

connect(this, SIGNAL(requestOpenDB(QString)), dbtrad, SLOT(openDB(QString)));
thread->start();

emit requestOpenDB(userID);

Upvotes: 3

Views: 4133

Answers (4)

Marek R
Marek R

Reputation: 37512

Reading documentation AND logs is essential!!!
In log you have a warning that YOU CAN"T MOVE TO THREAD IF OBJECT HAVE A PARENT. Also documentation clearly says that:

Changes the thread affinity for this object and its children. The object cannot be moved if it has a parent. Event processing will continue in the targetThread.


Proper way to fix it:

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    thread = new QThread(this);
    dbtrad = new dbThread(); // NO PARENT
    dbtrad->moveToThread(thread);

    // run object method in thread assigned to this object:
    QMetaObject::invokeMethod(dbtrad, "openDB", Qt::QueuedConnection, Q_ARG(QString, userID));

    connect(dbtrad, SIGNAL(clearPreviousHistory()), this, SLOT(clearHistoryV()));
    thread->start();
}

MainWindow::~MainWindow()
{
    dbtrad->deleteLater();
    thread->quit();
    thread->wait(5000); // wait max 5 seconds to terminate thread
}

Upvotes: -1

UmNyobe
UmNyobe

Reputation: 22890

dbtrad->openDB(userID); will execute like any normal function (Why should it?), in the GUI thread.

moveToThread allow you to execute slots called using signals in a separate thread.

If you want to execute openDB in the thread you can trigger its execution using

 connect (thread, SIGNAL(started()), dbtrad, SLOT(openDBWithUIDAlreadySet()))

or

 connect (this, SIGNAL(requestOpenDB(int)), dbtrad, SLOT(openDB(int)))

You need to use existing or additional signals. Qthread::start() emit the signal started(). You can also define

MainWindow{

signals:
    void  requestOpenDB(int);
    void queryHistory(int channelid);
}

and emit the signals manually using

emit requestOpenDB(userID);    //for openDB
emit queryHistory(channelId);  // for getHistory

the responses from the dbThread object also need to be given using a signal which is connected to a slot. Like a notification.

Upvotes: 2

Abhishek Bansal
Abhishek Bansal

Reputation: 5335

Yes so qt moveToThread() does not do what you are expecting it to do. The function that you are calling from your main thread will get executed in your main thread only. That database access is causing GUI freezes.

moveToThread only moves "event processing" in a seperate thread. Which means any slots of dbThread which are connected using Qt::QueuedConnectionwill get executed in new thread.

Following way will execute getHistory() method in your main ui thread only. You need to create a signal in main thread and make getHistory() a slot of dbThread class. Then connect both.

Upvotes: 0

Zaiborg
Zaiborg

Reputation: 2522

  1. QTcpSocketdoes indeed not need to be in a separated thread.

  2. as long as all the database access is done from that thread where the database was created it should also be no problem

  3. And now to the fun part: i think you create the database in the main thread ... by calling dbtrad->openDB(userId)

Upvotes: 0

Related Questions