PDA

View Full Version : Slot in main thread (gui), signal from background thread - how?



giaur
27th November 2009, 20:38
Hello,

I have simple gui application. This application creates new background thread. There are some operations to execute in the thread with progress reporting to gui thread.

Some code:
backthread.h:


#ifndef BACKTHREAD_H
#define BACKTHREAD_H

#include <QThread>
#include "Counter.h"


class BackThread : public QThread
{
public:
virtual void run();

private:
Counter counter;


};

#endif // BACKTHREAD_H

backthread.cpp:


#include "backthread.h"

void BackThread::run()
{
int i=0;
while(1)
{
counter.setValue(i);
sleep( 1 );
qDebug( "Ping!" );
i+=10;
if(i==100) i=0;
}
}


Counter.h:


#ifndef COUNTER_H
#define COUNTER_H


#include <QObject>

class Counter : public QObject
{
Q_OBJECT

public:
Counter() { m_value = 0; }

int value() const { return m_value; }

public slots:
void setValue(int value);

signals:
void valueChanged(int newValue);

private:
int m_value;
};

#endif


Counter.cpp:


#include "Counter.h"

void Counter::setValue(int value)
{
if (value != m_value) {
m_value = value;
emit valueChanged(value);
}
}


And in main windows class:


#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);

BackThread th;
th.start();
}

MainWindow::~MainWindow()
{
delete ui;
}


There is Progress Bar widget in main window, and its value should be updated when Counter::m_progress changes. But I have no idea how to define slot for main window class to receive notifications from thread.

Can you help me?

wysota
27th November 2009, 20:49
Your construction is incorrect. Objects that are members of QThread subclass live in the thread where the QThread object was created, not in the one the QThread object controls. You are likely to run into trouble later. To avoid the trouble you can move the QThread object to the thread it contols using QObject::moveToThread().

Now for your question - there is nothing special you have to do, just create a slot in your window that will take an integer and then issue a connect statement between counter's and your window's signals/slots. Just remember you need pointers to both objects available in the same moment. It might be easier if you declare a signal in your QThread subclass and connect counter's signal to the thread controller's signal and then connect the thread's signal to a slot in main window.

giaur
27th November 2009, 21:13
To avoid the trouble you can move the QThread object to the thread it contols using QObject::moveToThread().


Hmm ok, but where or when I should call this method? In Counter constructor, call from gui thread, etc...?



It might be easier if you declare a signal in your QThread subclass and connect counter's signal to the thread controller's signal and then connect the thread's signal to a slot in main window.

Do you mean I sholud define slot in BackThread and connect signal from Counter to it, and then emit signal from slot in BackThread to catch it in gui thread?

I'm not really sure why I need Counter class? What if I try to emit signal from BackThread::run instead of using Counter class?

Sorry for stupid questions, I'm totally new in QT...

wysota
27th November 2009, 21:18
Hmm ok, but where or when I should call this method? In Counter constructor, call from gui thread, etc...?
Hmm... actually it just came to my mind it's not enough. You need to move the Counter object as well. You can do it at any time after you call QThread::start() but you have to do it from within the main thread (you can only push an object to a different thread, not pull it in from another thread).


Do you mean I sholud define slot in BackThread and connect signal from Counter to it, and then emit signal from slot in BackThread to catch it in gui thread?
No, you can connect one signal to another signal. This way when one is fired, the other one is emitted as well (we call it signal propagation).


I'm not really sure why I need Counter class? What if I try to emit signal from BackThread::run instead of using Counter class?
You don't need it. Unless you want it to do something else besides holding that integer.


Sorry for stupid questions, I'm totally new in QT...
That's fine. Just remember it is Qt not QT. The latter is technology of a known fruit company.

giaur
27th November 2009, 21:38
You need to move the Counter object as well. You can do it at any time after you call QThread::start() but you have to do it from within the main thread
Should I create method in BackThread, that will perform this, and call it from main thread after QThrea::Start is called?


No, you can connect one signal to another signal. This way when one is fired, the other one is emitted as well (we call it signal propagation).
Hmm, I'm little confused now. Could you add this to code I posted, with some explanation?

wysota
27th November 2009, 22:13
Should I create method in BackThread, that will perform this, and call it from main thread after QThrea::Start is called?
Just call it right after you call start() on the thread.



Hmm, I'm little confused now. Could you add this to code I posted, with some explanation?


class Counter : public QObject {
Q_OBJECT
public:
Counter() { m_counter = 0; }
void setValue(int v) {
if(m_counter == v) return;
m_counter = v;
emit valueChanged(v);
}
signals:
int valueChanged(int);
};

class Thread : public QThread {
Q_OBJECT
public:
Thread() : QThread() {
connect(&counter, SIGNAL(valueChanged(int)), this, SIGNAL(counterValueChanged(int)));
}
void run() { ... }
void moveObjectsToThread() {
moveToThread(this);
counter.moveToThread(this);
} // required to get around private scope
signals:
void counterValueChanged(int);
private:
Counter counter;
};

// ...
Thread thread;
connect(&thread, SIGNAL(counterValueChanged(int)), ...);
thread.start();
thread.moveObjectsToThread();

giaur
28th November 2009, 11:45
This doesn't work:



Thread thread;
connect(&thread, SIGNAL(counterValueChanged(int)), ...);
thread.start();
thread.moveObjectsToThread();


No signals emiited at all and iwarning in console is:


QThread: Destroyed while thread is still running


But this works fine:


Thread *thread;
connect(thread, SIGNAL(counterValueChanged(int)), ...);
thread->start();
thread->moveObjectsToThread();


Thread is created and started in main window constructor. In the first case - QThread object is destroyed without stopping thread. So, the thread works, but without any control ("Ping" commands are printed as weel). Why?

squidge
28th November 2009, 12:09
The first thread will be destroyed when the variable goes out of scope, whilst the second will not.

giaur
28th November 2009, 19:42
Ok, understood... another question - should I delete thread object? Or it is disposed already after thread finishes?

wysota
28th November 2009, 20:31
The QThread object has nothing to do with the lifetime of the thread it controls. It's an object like any other. If it was autodeleted, all applications allocating QThread objects on stack would not work.