user2290362
user2290362

Reputation: 717

Packaging a threaded program with Qt GUI

I have a working, finished application which uses multiple boost threads and works fine with a command line interface.

I have packaged this program with a "wrapper" class so that I can run the functions within the program from a Qt Main Window.

For example:

netWrap.getImgs(ui->sampleNameEdit->text().toStdString());

This gets images from the network program with the parameter sampleName from a textbox and is called from a slot triggered by a pushbutton.

This all works fine, however, some functions, such as the one above, can take about 20 seconds to run and this hangs the GUI. I'd like it to not hang the GUI.

The obvious choice is to use a QThread, but I don't want to add extra classes to my program as this is only supposed to be a simple front end. Is there a way that I can run this function in a thread and wait for a termination signal without hanging the GUI?

EDIT: QFutures:

 QFuture<int> run = QtConcurrent::run(&(netWrap.getImgs), ui->sampleNameEdit->text().toStdString());

This produces 4 errors, the most relevant are probably:

error: ISO C++ forbids taking the address of a bound member function to form a pointer to member function.  Say '&networkWrapper::getImgs' [-fpermissive]
     QFuture<int> run = QtConcurrent::run(&(netWrap.getImgs), ui->sampleNameEdit->text().toStdString());

and:

error: no matching function for call to 'run(int (networkWrapper::*)(std::string), std::string)'
     QFuture<int> run = QtConcurrent::run(&(netWrap.getImgs), ui->sampleNameEdit->text().toStdString());

Upvotes: 3

Views: 481

Answers (2)

Nejat
Nejat

Reputation: 32645

You should provide the pointer to the object and also the address of the class member function and the desired parameters like :

QFuture<void> future = QtConcurrent::run(netWrap, &networkWrapper::dbsChanged, ui->sampleNameEdit->text().toStdString());

You can check the state of the asynchronous computation represented by the future like:

if(future.isRunning())
{
    // It is currently running
}

Or wait for it to finish:

future.waitForFinished();

QtConcurrent::run() does not provide progress information automatically like QtConcurrent::mappedReduced(). But you can have your own progress reporting mechanism using signals.

To inform about termination, emitting a signal from the function which shows it has finished is a straightforward way .

Upvotes: 1

user3427419
user3427419

Reputation: 1779

You do not subclass QThread to run something in a different thread. QThread is used via signals/slots.

http://doc.qt.io/qt-5/qthread.html#started

Default QThread::run() starts an event loop allowing you to process things like network events in the other thread. This means you connect to QThread::started signal, initiate your requests and they get processed in a different thread. Then you can signal your main GUI thread with another signal that you are done and data is available.

If all you need is to call some function in a different thread, then use Qt Concurrent. This is much easier to use than QThread.

http://doc.qt.io/qt-5/qtconcurrent.html#run

void my_function(int a, double b) {
     .... do something in another thread ....
}
    ....
    auto some_function = std::function<void(int, double)>(&my_function);
    QFuture<void> ret = QtConcurrent::run(some_function, 10, 20.0);
    ....

Or without any use of std::function, just use plain function pointers directly,

QFuture<void> ret = QtConcurrent::run(&my_function, 10, 20.0);

To call class members, it's also just as simple,

class Foo {
public:
     void foo(int a) {}
};

...
Foo *foo_instance = new Foo;
QFuture<void> ret = Qtconcurrent::run(foo_instance, &Foo::foo, 10);
...

And your function is running in another thread. It's that easy.

NOTE: Do not access/manipulate GUI classes from non-GUI threads.

Upvotes: 3

Related Questions