PDA

View Full Version : QThread slot executed in GUI thread



tnyblom
24th May 2010, 10:21
Hi,

I've got a GUI application (QApplication) that has a QMainWindow as the GUI. In this I have a button that triggers an action that should be executed in a thread. To achieve this I connect the buttons clicked() signal to a slot in the thread object. However the threads action is executed in the GUI thread.

Problem can be seen with the code below. On (at least) the second click on the button in the GUI I want the GUI to remain responsive and the thread to sleep.

Does anyone have an idea as to what I am doing wrong?

main.cpp

#include <QtGui/QApplication>
#include <QDebug>
#include "mainwindow.h"

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
qDebug() << "main:" << a.thread();
return a.exec();
}

thread.h

#ifndef THREAD_H
#define THREAD_H

#include <QThread>

class Thread : public QThread
{
Q_OBJECT
public:
explicit Thread(QObject *parent = 0);
void run();

signals:
void WorkDone();

public slots:
void DoWork();
};

#endif // THREAD_H

thread.cpp

#include "thread.h"

#include <QDebug>

Thread::Thread(QObject *parent) :
QThread(parent)
{
qDebug() << "Creating new Thread" << QThread::currentThreadId();
}

void Thread::run()
{
qDebug() << "run:" << QThread::currentThreadId();
exec();
}

void Thread::DoWork()
{
qDebug() << "Thread::DoWork() in thread:" << QThread::currentThreadId();
sleep(10);
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include "thread.h"

#include <QMainWindow>

class MainWindow : public QMainWindow
{
Q_OBJECT

public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();

private:
Thread thread;
};

#endif // MAINWINDOW_H


mainwindow.cpp

#include "mainwindow.h"

#include <QtGui>
#include <QDebug>

MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
qDebug() << "MainWindow:" << this;
QWidget *widget = new QWidget();
QHBoxLayout *layout = new QHBoxLayout(widget);
QPushButton *button = new QPushButton("Do work...", widget);
widget->setLayout(layout);
layout->addWidget(button);
setCentralWidget(widget);
thread.start();
connect(button, SIGNAL(clicked()), &thread, SLOT(DoWork()));
}

MainWindow::~MainWindow()
{
thread.exit();
thread.wait();
}

tbscope
24th May 2010, 10:28
You never start another thread.

Implement QThread::run() in your own thread.
Then use QThread::start() to start your thread.

http://doc.qt.nokia.com/4.6/threads-starting.html

tnyblom
24th May 2010, 10:29
Sorry that was my mistake, please see the post again (I've updated it), but the problem persists.

tbscope
24th May 2010, 10:37
Now you have actually started a new thread and executed the run() function once.
If you call DoWork(), the thread doesn't actually execute what is in the run() function.

Try calling run() from within your DoWork() function.

tnyblom
24th May 2010, 10:48
But I do not wan't to execute the run function I wan't to execute something else (exec() in run is due to the use of QEvents).

If I call run() from DoWork() will that not create problems since Thread will have multiple invocations of run() running at the same time? How will this affect Event handling?

tbscope
24th May 2010, 10:55
But I do not wan't to execute the run function I wan't to execute something else (exec() in run is due to the use of QEvents).
I suggest you read more about threading in Qt. There's a lot of information on the documentation website.


If I call run() from DoWork() will that not create problems since Thread will have multiple invocations of run() running at the same time? How will this affect Event handling?
Well duh! :-)
Threading in general isn't very easy :-)

The run() function contains the code that is actually being executed by your thread.
In most cases, an infinite loop is created in the run function. Then mutexes and waitconditions are used to queue the requests for the thread.
See the examples.

borisbn
24th May 2010, 12:33
add this code in run() function

moveToThread( this );

tnyblom
24th May 2010, 13:00
Thanks for the effort to all, but nothing does what I want.

I need/want to issue a signal in one thread and get another to pickup the slot. Is this possible?
So far all my attempts have resulted in the slot being run in the same thread that sent the signal but in the context of the slot object.

squidge
24th May 2010, 13:29
As already explained, 'moveToThread' will move the event handling from the thread that sent the signal to the thread where the slot is.

(Assuming you actually run an event loop in the new thread of course)

tnyblom
24th May 2010, 14:25
Well not here (at least not as I understand it) :(

If I use moveToThread() in run() I get an error
"QObject::moveToThread: Current thread (0x12fecc) is not the object's thread (0xd47550).
Cannot move to target thread (0x12fecc) "

and if I use moveToThread() in my slot I get an ASSERT in qglobal.cpp:2233 "Cannot send events to objects owned by a different thread".

tnyblom
24th May 2010, 14:34
Found the root cause:
(hint from: http://huntharo.com/huntharo/Blog/Entries/2009/8/21_QThread_signals_slots_-_Why_your_calls_stay_in_the_main_thread.html)

I need to call moveToThread() in the Thread ctor after calling start().

borisbn
24th May 2010, 15:52
tnyblom, sorry, you must call moveToThread(this) in thread's constructor

tnyblom
25th May 2010, 07:09
And that was what I wrote.

More specifically I need to call moveToThread() in the context of the thread that created the thread, and supply the threads thread object as the param.

squidge
25th May 2010, 07:49
More specifically I need to call moveToThread() in the context of the thread that created the thread, and supply the threads thread object as the param.Which you can easily forget to do, which is why it's much easier to just use moveToThread(this) in the threads constructor, then it's guaranteed to be called in the context of the thread that created the thread, even if you create multiple copies from different parts of your code.