PDA

View Full Version : Adding new Widgets in a seperate thread



Cruz
16th January 2009, 13:05
Hi!

I'm meaning to grab some data from a network connection, wrap the data into a QFrame and add those QFrames to my gui. The data comes slow, so it has to be a seperate process that doesn't block the gui. Also the user needs to have the possibility to stop the process in the middle. So what I did is this:



class FrameGrabber : QThread;

void GUI::on_startGrabButton_clicked()
{
// Start the grabber thread.
frameGrabber.start();
}


And inside the frameGrabber thread:



class MyFrame : QFrame;

void FrameGrabber::run()
{
int framesToGrab =1000;

while(framesToGrab >= 0)
{
MyFrame* f = NetworkConnection::grabFrame();
GUI->addFrame(f);
framesToGrab--;
msleep(10);
}

GUI->grabbingProcessFinished();
}


The NetworkConnection class will instantiate a MyFrame, which is derived from QFrame. However, since the NetworkConnection doesn't know anything about the GUI, it cannot pass QWidget* parent as an argument to the constructor, so it can't instantiate a full blown QFrame yet. Only when MyFrame is added to the GUI with GUI->addFrame(), I try to set the parent with the setParent() method:



void GUI::addFrame(MyFrame* f)
{
f->setParent(ui.GrabbedFrameBufferArea);
}


This was the plan anyhow, but it doesn't work. I get a lot of this:



QPixmap: It is not safe to use pixmaps outside the GUI thread
QObject::setParent: Cannot set parent, new parent is in a different thread


I don't quite get why it complains about a different thread adding Widgets to the gui. But in any case, what would be the right solution?

Thanks
Cruz

jpn
16th January 2009, 13:10
Use QImage in a worker thread, deliver it to the main GUI thread by using signals and slots, and create the frame in the GUI thread. You cannot use QPixmap or any widgets in worker threads.

Cruz
16th January 2009, 14:19
Yes, good solution thaks!

wysota
16th January 2009, 14:27
Also consider the option of doing it all in a single thread. You don't need a separate thread just to receive data over network.

Cruz
16th January 2009, 14:48
And how can I accomplish that? The data transfer is triggered by a button. So first thing that happens is that a signal (clicked()) is sent into a slot. Coming from Java Swing I was raised to return an event handler as quickly as possible, otherwise I will keep the gui thread busy and the gui itself will freeze. So this is why I decided to start a thread from there and to return the slot as quickly as possible. So unless Qt itself takes care of splitting out a thread to handle the slot, so that the gui is not blocked by my sluggish program, I don't see how I can solve it without a thread.

wysota
16th January 2009, 14:58
Longer explanation is here:
http://doc.trolltech.com/qq/qq27-responsive-guis.html

In short, network transfer is asynchronous - you schedule some request and then return from the function and Qt will notify you using a signal that the data is ready. You can then process it (read it to a buffer or process immediately) and return from the function. When more data arrives, you will be notified again. In the meantime the application can do other things (like refresh the user interface or make coffee).

Cruz
16th January 2009, 16:07
I see where you are coming from. Well, actually it's not really a network connection, it's a more complicated custom USB communication with a robot. I simplified that detail in my question. It means that I can't take advantage of the solid network com solutions that Qt offers. And with that being gone, I personally think that the worker thread is the most elegant solution from all the alternatives proposed on that page.

wysota
16th January 2009, 18:20
I see where you are coming from. Well, actually it's not really a network connection, it's a more complicated custom USB communication with a robot.
That doesn't change anything. It's a remote data source, that's all that counts.


It means that I can't take advantage of the solid network com solutions that Qt offers.
Sure you can. You can read from USB in an asynchronous way, you can even use QExtSerialPort probably and there are always timers. Threads are really not required here.


And with that being gone, I personally think that the worker thread is the most elegant solution from all the alternatives proposed on that page.

Well... Java itself is said to be elegant and that probably ends its advantages over other languages. Doing everything in threads is a nice separation of code but it is often crude when it comes to communication with other components and it adds some overhead to the application. Using signals and slots and event-driven flow gives you the same separation without the down sides. Sure, it might look hard at first but once you grasp it, it becomes very natural.