PDA

View Full Version : Help with QThread



manucorrales
8th October 2006, 15:42
Hi, i am new in thread programming with c++. Ive used threads in Java.

My problem is this:

I have a class with several methods. All this methods MUST be runned as thread because attemps to connect to external devices and i dont want to block my app. If i subclass QThread then i must have a run method reimplemented. Now the ugliest solution would be a huge case or something like that to call the method i want. But have to be another way. In hava there are anonymous classes so is easier. How can i solve this??


Thanks very much.

jacek
8th October 2006, 16:38
If i subclass QThread then i must have a run method reimplemented. Now the ugliest solution would be a huge case or something like that to call the method i want.
I'm not sure what's the problem since there is no difference between C++ and Java in that matter (except for the built-in monitor, which is present in Java).

You can always avoid switch statements using a command pattern (i.e. with a set of objects resembling jobs/commands and a queue with proper synchronization).

manucorrales
8th October 2006, 17:07
Thanks. Can you be a little more specific on the Command Pattern? I have this:

Class A
{
void method1(){};
void method2(){};
void method3(){};
}


and when i call a method of an object of this class, must run in background. The only solution i find so far is to make an inner sublclass of QThread inside each method and in the reimplemented run method put my code. Then create myThread and call start.

manucorrales
8th October 2006, 17:13
Maybe i am doing this wrong. My initial problem is this: i have to call a methos wich tries to connect to an fiscal printer. I would like to show a dialog that says: "Trying to cennect, please wait" blocking the user but with the possibility of cancel. While showing this dialog, the method i running and trying to connect. Then, when the connection fails or success do something else.

Thanks for your help!

jacek
8th October 2006, 17:55
I would like to show a dialog that says: "Trying to cennect, please wait" blocking the user but with the possibility of cancel. While showing this dialog, the method i running and trying to connect. Then, when the connection fails or success do something else.
Then you have two options:
use non-blocking I/O and QApplication::processEvents(),
create another thread that will communicate with the printer.

If you use the second approach, all you need is signals & slots mechanism and queued connections. When you invoke a slot through queued connection, Qt will send an event to the thread and it will pick it up and invoke proper slot. Then you can emit a signal that operation was completed.

Another way is to subclass QEvent and create classes that represent "commands" like ConnectEvent or similar and send it to the thread (this way you reuse the Qt event loop and you don't have to bother with synchronization). Those new events can share a common interface, so your thread might look like this:

void PrinterThread::run()
{
exec();
}

void PrinterThread::customEvent( QEvent *event )
{
CommandEvent *cmd = dynamic_cast< CommandEvent *>( event );
if( cmd != 0 ) {
cmd->perform( state ); // state holds all necessary information, like file descriptors
}
else {
// error
}
}
This way you don't need any switch statements.

But IMO the approach with signals & slots is much easier to implement (and de facto it's the same thing --- only Qt implements part of it for you). Just don't forget about invoking exec() and that the connections must be queued.

As for the non-blocking I/O: all depends on the way you communicate with the printer. If it expects you to answer within given time or it has a short output buffer, it might not be applicable. Also wrap it in some kind of singleton class, because such code can get a bit messy. The obvious benefit is that you don't use another thread just to communicate with the printer.

manucorrales
8th October 2006, 18:53
Where can i found info on the non blocking I/O and processEvents() ??

I cant find on the assistant.

jacek
8th October 2006, 19:01
Where can i found info on the non blocking I/O and processEvents() ??
Here:
http://doc.trolltech.com/4.1/qcoreapplication.html#processEvents
http://doc.trolltech.com/4.1/qsocketnotifier.html

and also `man 2 select`.

manucorrales
12th October 2006, 17:31
Sorry i keep asking you. Can you explain to me a little more the queue and slot and signals approach?

Thanks!!

jacek
14th October 2006, 21:25
Can you explain to me a little more the queue and slot and signals approach?
Sure. It's quite easy:

class PrinterThread : public QThread
{
Q_OBJECT
public:
...
public slots:
void foo( const QString& str );

signals:
void bar();
...
};

void PrinterThread::run()
{
exec(); // <- this is very important
}

void PrinterThread::foo( const QString& str )
{
...
emit bar();
...
}

...
connect( someObject, SIGNAL( someSignal( const QString& ) ), printerThread, SLOT( foo( const QString& ) ), Qt::QueuedConnection );
connect( printerThread, SIGNAL( bar() ), someObject, SLOT( barReceiver() ), Qt::QueuedConnection );
...
As you can see you don't need much to get it work.

When you invoke foo() slot through a queued connection (i.e. using a signal --- don't even try to invoke it directly), Qt will create a QEvent object, copy all parameters and post that event to the PrinterThread event loop. Later PrinterThread will pick that event, invoke the foo() slot and return back to the event loop to wait for another event --- and that's all.

If you want to subclass QThread (as in the above example), make sure that you use queued connections (direct and automatic connections won't work).

Also you can try to do it like this:

// implements the communication protocol
// only PrinterThread should use it
class PrinterController : public QObject
{
Q_OBJECT
public:
...

public slots:
void doSomething();

signals:
void done();
...
};

// provides printer interface to the rest of the application
class PrinterThread : public QThread
{
Q_OBJECT
public:
...
signals:
void done();
void doSomething();
}

void PrinterThread::run()
{
PrinterController controller;
connect( this, SIGNAL( doSometing() ), &controller, SLOT( doSomething() ) );
connect( &controller, SIGNAL( done() ), this, SIGNAL( done() ) );
exec();
}

...
connect( printerThread, SIGNAL( done() ), someObject, SLOT( continueWork() ) );
printerThread->doSomething(); // this behaves just as a non-blocking call
...
With this approach you don't have to remember about adding Qt::QueuedConnection and you get a non-blocking interface to your printer.

Just remember that Qt will copy all parameters, even if you use const references. If it complains that you can't send SomeCustomType through queued connection, then it means that you have to register that type first with qRegisterMetaType() (search the forum for more info about this).