Solved qt Passing data from QThread to main thread
-
I'm trying to create a separate thread processing data then pass the result back to main thread. Here is what I'm thinking of doing:
class Worker : public Object { Q_BOJECT public: Worker(){ QThread* thread = new QThread; this->moveToThread(thread); }; ~Worker(); DataClass* data1; int data2 QThread* thread; public slots: void process(){ //do something to create DataClass* data1 and int Data2 moveToThread(QApplication::instance()->thread()); Q_EMIT ImDone(this); }; signals: void ImDone(Worker * data); } class MainWin : public QMainWindow { Q_BOJECT public: MainWin(){ QTreeWidget* tree=new QTreeWidget(); }; ~MainWin(); void callWorker(){ Worker* worker = new Worker(); connect(this, SIGNAL(startWorker()), worker, SLOT(process())); connect(worker, SIGNAL(ImDone(Worker *)), this, SLOT(processWorkerResult(Worker *))); Q_EMIT startWorker(); }; QTreeView* tree; public slots: void processWorkerResult(Worker *worker){ //process Worker data1 and data2, update QTreeWidget* tree }; signals: void startWorker(); }
The thought is that, I create the worker thread in main thread, start the worker by sending it a signal (MainWin::startWorker()), worker thread starts the work and then sends a signal with pointer to data (Worker::ImDone(Worker*)) back to main thread. Main thread then processes the data and update its component (say a QTreeWidget). Does this look ok?
Can someone also explain how the signal/slot event is handled in main thread? If user start two different workers which ends up finishing at the same time (literally), will that cause two instances of MainWin::processWorkerResult to be run over each other? If so that seems to be a problem... how to resolve? Thanks.
-
@Pauly said in qt Passing data from QThread to main thread:
event is handled in main thread? If user start two different workers which ends up finishing at the same time (literally), will that cause two instances of MainWin::processWorkerResult to be run over each other?
You already answered it yourself - it's running in the main thread so how should two instances run in parallel then?
-
A little hard to follow but what is stopping you from doing this for your finished signal?
void ImDone(DataClass data1, int data2);
instead of
void ImDone(Worker * data);
I believe there is something called 'sender' which basically will give a QObject * to the sender which can be cast to your Worker * already anyways.
This isn't quite how I would do it but to keep it somewhat like your original idea these are some things I would add/change.
Worker() { QThread* thread = new QThread; this->moveToThread(thread); connect(thread, &QThread::started,this, &Worker::process); connect(this, &Worker::ImDone, thread, &QThread::quit); connect(thread, &QThread::finished, thread, &QThread::deleteLater); connect(thread, &QThread::finished, this, &Worker::deleteLater); thread->start(); //here your worker will basically start once you create it and delete itself along with its thread when it is complete. }; void process() { DataClass data1; int data2; //do something to create DataClass data1 and int Data2 //moveToThread(QApplication::instance()->thread()); //not necassary with signals and slots Q_EMIT ImDone(data1,data2); }; ----------------------------------------- void processWorkerResult(DataClass data1, int data2) { //process Worker data1 and data2, update QTreeWidget* tree // because your thread and worker will now be deleted, i do not suggest sending pointers do data. Unless your absolutely have to because of reasons I don't know. If that is the case then make sure you properly stop the thread and delete your worker/thread some other way. };
Above i said it is not how I would do it - by that I mean I would not bake the thread functionality into the class but design the class to be thread safe and move to thread outside of the class and also do the thread signal and slot connections outside as well.
-
I was thinking of doing void ImDone(Worker * data); because there are more than a few variables in Worker that I need to pass back. Let me try casting the sender to Worker*, if that works then I don't need to pass anything in the signal.
You are right on the point. I'd like to be able to pass that worker back to main thread and use its data therefore I don't want the worker to be deleted when the "process" is done running because I may want to run other function in a thread... You are right that combining the thread and data seems a bit messy. How about this:
class DataStructure { public: DataStructure(); ~DataStructure(); QString* data1; int data2 public slots: void process1(){ //do something to generate data, data2 and etc moveToThread(QApplication::instance()->thread()); //need this to avoid data being deleted when thread is done? Q_EMIT ImDone1(); }; void process2(){ //do something to generate another set of data, data2 and etc moveToThread(QApplication::instance()->thread()); Q_EMIT ImDone2(); }; class MainWin : public QMainWindow { Q_BOJECT public: MainWin(){ QTreeWidget* tree=new QTreeWidget(); }; ~MainWin(); void callWorker(){ DataStructure* data=new DataStructure(); QThread* thread = new QThread(); data->moveToThread(thread); connect(this, SIGNAL(startProcess1()), data, SLOT(process1())); connect(data, SIGNAL(ImDone1()), this, SLOT(processWorkerResult())); connect(this, SIGNAL(startProcess2()), data, SLOT(process2())); connect(data, SIGNAL(ImDone2()), this, SLOT(processWorkerResult())); Q_EMIT startProcess1(); //Q_EMIT startProcess2(); or start at different places }; QTreeView* tree; public slots: void processWorkerResult(){ //cast the sender and use the data //process data and update QTreeWidget* tree }; signals: void startProcess1(); void startProcess2(); }
-
@Christian-Ehrlicher Thank you for confirming that. I wasn't sure if the "processWorkerResult" is actually running in main thread or in worker thread.
-
@Pauly said in qt Passing data from QThread to main thread:
You are right on the point. I'd like to be able to pass that worker back to main thread and use its data therefore I don't want the worker to be deleted when the "process" is done running because I may want to run other function in a thread... You are right that combining the thread and data seems a bit messy. How about this:
So send the data class and not the worker class. In my example you will send a copy of the data class back to the main thread. The worker, its thread, and its copy of the data are deleted but through the signal a copy of data is saved and sent back to your main thread.
-
@MrShawn Thanks. There is only one small problem... I try to store the copy of DataClass data1 in "processWorkerResult", by "DataClass storeData=data1", but it throws error saying "call to implicitly-deleted copy constructor". Am I missing something in my DataClass?
Also I wonder if it is ok to send just the pointer of data in signal "process"? Something like:
void process() { DataClass* data1=new DataClass(); //do something to create DataClass data1 and int Data2 data1->moveToThread(QApplication::instance()->thread()); // Do I need this? Q_EMIT ImDone(data1); };
if by moving data1 to main thread can preserve it when the thread is deleted, that may work for me... also curious what happens to data1, will it be deleted only when my main thread finished (quit application)? Thanks.
-
@Pauly said in qt Passing data from QThread to main thread:
will it be deleted
You have to delete it by your own since you created it with new and stored it in a raw pointer
-
My guess as to what your issue is is that Your DataClass (or some of its members) inherits from QObject which actually has its copy constructor deleted on purpose.
Does what you are doing with DataClass require for you to inherit from QObject? Can you post your DataClass code? What are some more details as to what you are trying to process with the worker, and what you need to be sending back to your main thread?
I do not see anywhere where you delete your object.
DataClass* data1=new DataClass(); data1->moveToThread(QApplication::instance()->thread()); // Do I need this? Q_EMIT ImDone(data1);
I see you create a new object and you do have a pointer that you send out via a signal, so back in your main you will need to delete it when you are done working with it.
-
@Pauly
I like passing data between threads using QSharedDataPointer or QSharedPointer. This is a very safe way to handle the data and makes it more asynchronous on when and how it gets deleted.Of course, if you only use it in your event handler the data will decrement the reference and remove the data if the count is 0. It is also nice because you can allocate it and Q_EMIT it which will keep the memory alive in the event queue even if your emitter goes out of scope.
Example:
class Xyz { ... };
typedef QSharedPointer <Xyz> PXyz;--- in your main --- (this may not be necessary using new methodology for connect)
qRegisterMetaType <PXyz> ("PXyz");--- in your thread header ---
Q_SIGNALS:
void toMainThread (const PXyz& xyz);--- in your thread source ---
void MyThread::doSomething ()
{
PXyz xyz = new Xyz;
//DO SOMETHING
Q_EMIT toMainThread (xyz);
}--- in your main header
<access> Q_SLOTS:
void fromThread (const PXyz& xyz);--- in your main source ---
connect (thread, &MyThread::toMainThread, this, &MyObject::fromThread);
void MyObject::fromTHread (const PXyz& xyz)
{
xyz->implement (this);
}This way really keeps things nice and simple with minimal overhead. I handle hundreds of items/second this way.
-
@MrShawn Yeah, DataClass inherits from QObject, because I was trying to setup signal/slot in it. It doesn't have to since I decouple it from the worker. I think I can make pointer works so storing DataClass is unnecessary any more. Thanks!
-
@Buckwheat This is great idea. Thanks a lot!