3

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());
Nejat
  • 31,784
  • 12
  • 106
  • 138
user2290362
  • 717
  • 2
  • 7
  • 21
  • 1
    Can your wrapper class inherit QThread, and then run as a QThread in the background? The only other option would be to kick off a different kind of thread. I don't see how adding classes is a bad thing. Then again, I don't see the whole picture. – kiss-o-matic Dec 29 '14 at 18:07
  • Will this allow me to run the function only at a specific time? And run other functions too? And wait for them to terminate? – user2290362 Dec 29 '14 at 18:08
  • When you subclass QThread, you reimplement run(). It does everything in that function in another thread (but the object belongs to the GUI thread). It might seem a little off coming from boost or pthread, so I'd recommend reading up on it and making sure it's what you want. You can easily communicate w/ the GUI thread via signals & slots though. So yes, you can run only that function at a given time. You pass your object the string, and use a signal/slot connection to tell it to start (and stop when it's done). – kiss-o-matic Dec 29 '14 at 18:20
  • If my wrapper class had a hundred functions though, not just one, this would not work without implementing a state machine as the run function? Surely there's an easier way to just run a function in a thread and know when it terminates...? – user2290362 Dec 29 '14 at 18:32
  • I think you're getting into design questions now. It might be easier for your wrapper to have a subclassed QThread. Or, it can of course kick off a pthread (or boost thread) to do the work. I've not used boost's threads. I've never delved into the implications of having other types of threads need to talk to the GUI thread, so for what little threading I've done in Qt, I've stuck w/ QThread. – kiss-o-matic Dec 29 '14 at 19:21

2 Answers2

3

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.

user3427419
  • 1,769
  • 11
  • 15
1

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 .

Nejat
  • 31,784
  • 12
  • 106
  • 138