QT - Show Widget without blocking his GUI - multithreading

I want to show a widget for displaying an animated loading gif while another function (pthread) computes a task.
I have tried to subclass my widget with a QThread class, and implemented the method run() where I call the method show(). However, my widget GUI froze.
How can I launch a widget where the GUI is processed separately?

You can't have widgets running on anything except the main thread.
Also, unless you're wanting to change how Qt handles threads, you should not be inheriting from QThread.
Instead, create a worker object that inherits from QObject and move that to the new thread. You can read how to really use QThread here.
Your worker object can then be moved to another thread, do its computation and communicate to the Gui widgets, on the main thread, via signals and slots.
For example, here's a brief outline of a worker class: -
class Worker : public QObject
{
Q_OBJECT
signals:
void finished();
void displayWidget();
private slots:
void run();
}
QThread pThread = new QThread;
Worker pObject = new Worker;
// move the pObject to the thread
pObject->moveToThread(pThread);
You can then control the thread with signals and slots.
// assuming you've added a run slot function to the Consumer class
connect(pThread, SIGNAL(started()), pObject, SLOT(run()));
connect(pObject, SIGNAL(finished()), pThread, SLOT(quit()));
connect(pObject, SIGNAL(finished()), pObject, SLOT(deleteLater()));
// Note the thread cleans itself up here, but if the app is quitting,
// waiting on the thread to finish may be required instead
connect(pThread, SIGNAL(finished()), pThread, SLOT(deleteLater()));
And start the thread: -
pThread->start();
Used this way, it also enables multiple objects to be moved to a single new thread, rather than creating a new thread per object instance.
So now, for example, if you wanted to display a widget at some point during the processing in the worker object, you'd emit its displayWidget() signal, having previously connected it to the Widget's show() slot.
QWidget* pWidget = new QWidget(parent); // assumes parent is initialized
// using Qt 5 connect call
connect(pWorker, &Worker::displayWidget, pWidget, &Widget::show);

You can't use QWidget (nor any derived classes) directly from threads other than the GUI thread. All you can do directly is to use a QImage owned by the worker thread and paint on it directly from that thread. Here, directly means that you are simply calling methods of objects.
What you need is a way to execute show() not directly in the invoking thread, but indirectly within the GUI thread's context. This is quite simple since QWidget::show() is a slot. Thus, from your computation thread, simply do:
QMetaObject::invokeMethod(widget, "show");
That's all. The implementation of invokeMethod will determine that widget lives in a different thread, and will automatically choose the QueuedConnection method of call delivery. Internally, it will post a QMetaCallEvent to widget. The widget's QObject::event method will act on the event and place the call to the show method. This will happen in the GUI thread.
You can use the same approach to set QProgressBar, for example:
int value = ...;
QMetaObject::invokeMethod(progressBar, "setValue", Q_ARG(int, value));

Related

Direct interaction between Qt/QML frontend and C++ backend/model in another thread

I have a Qt/QML (QT 5.7.1) application that needs to interact with a (quite complex) C++ backend, that lives in another thread.
Currently, I followed the suggestion from the blog article https://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/ and created a C++ class "MainController", that is registered via
qmlRegisterType<MainController>("MainController", 1, 0, "MainController");
and instantiated from the main QML file like this:
ApplicationWindow {
MainController {
id: mainController
}
...
}
In its constructor, the MainController (which runs in the same thread as the QML code) creates a Worker object, which is then moved into its own thread:
MainController::MainController() : QObject()
{
state = GUI_State::HW_STANDBY;
QThread* thread = new QThread;
Worker* worker = new Worker();
worker->moveToThread(thread);
connect(worker, SIGNAL (error(QString)), this, SLOT (errorString(QString)));
connect(thread, SIGNAL (started()), worker, SLOT (process()));
connect(worker, SIGNAL (finished()), thread, SLOT (quit()));
connect(worker, SIGNAL (finished()), worker, SLOT (deleteLater()));
connect(thread, SIGNAL (finished()), thread, SLOT (deleteLater()));
...
thread->start();
qDebug() << "Thread started";
}
Now, I can communicate using signals this way:
QML -> MainController -> Worker
QML <- MainController <- Worker
But there is a lot of overhead for this: to make a "property" of the Worker available to the GUI, I have to send a signal to the MainController, which needs a slot to receive the signal, which then triggers another signal, which then can be received from within the QML code. Same applies to the other direction.
How can I establish a direct link between the backend (Worker class) and the QML layer, preserving the two-thread architecture ?
If you dont use Q_PROPERTY, you can instance the worker itself in QML You can also make your worker-instance a context-property by
Worker* workerObj = new Worker();
engine->rootContext()->setContextProperty("qmlWorker", workerObj );
This will only work for Signal/Slot-Connection, aswell as Q_INVOKABLE.
If you want to use QML-Properties via a different Threads, I would suggest to use your approach as it is a save way to handle Threads in Qt in general.
According to qml c++ property bindings with multiple threads, one can't access properties that live in another thread.

How to cancel threads using QThreadPool

I can't share code but theres a sort of example below. Here's my problem. Imagine I have a controller class that has an instance of QThreadPool. This class keeps a list of worker objects. When I start a runnable, I create a worker and give it to the runnable. The runnable operates each worker on the different threads. When the runnable is done, it fires a signal back to the controller object. The controller object then reads the results from the worker and deletes the worker. The controller keeps a list of all workers so that it can set the cancel flag on the worker, so that the thread can exit early if necessary.
The problem is that the QThreadPool will wait for all threads to finish when it's destroyed, which will be in the controller destructor. Since signals are queued when fired from the threads, then potentially canceling the workers in the controller destructor will queue the signals to be delivered to the controller. But since the controller will be in the context of the destructor, those signals won't hit the controller slots, and therefore the worker objects won't get destroyed.
Maybe I'm not dealing with the thread pool the way it should be. If I destroy the worker objects in the runnable, then there is potential for the worker to be destroyed before the another thread tries to cancel a worker. Can anyone recommend a good way of creating a cancelable thread pool?
class Controller : public QObject {
Q_OBJECT
public:
virtual ~Controller();
QThreadPool pool;
void startTask();
Q_SLOT void onWorkerDone(Worker * worker);
QList<Worker*> workers;
void cancelAll();
}
void Controller::startTask() {
Worker * worker = new Worker();
connect(pool, SIGNAL(done(Worker *)), this, SLOT(onWorkerDone(Worker *));
workers << worker;
pool.start(new Runnable(worker);
}
void Controller::onWorkerDone(Worker * worker) {
if ( worker ) {
// read worker...
delete worker;
}
}
Controller::~Controller() {
cancelAll();
// Destructor now kills pool object.
// The pool will wait until all threads complete.
// If there are any workers still running on the
// other threads, their done() signals will be
// queued to this instance, and since we're in
// the dtor, they won't make it to the slot,
// and worker won't get deleted.
}
void Controller::cancelAll() {
// loop the list and call cancel.
}
First mistake. You should not create a QThreadPool. This is application whide object so you should use QThreadPool::globalInstance()
If you want to cancel job you have to provide some functionality in Worker class to do so. You need some loop in Worker::run() method which will do your job and pool for value which should change tread safely when loop should finish.
The simple solution is to subclass QThreadPool and add an aboutToWait signal to that class. The signal would need to be emitted in the destructor. You can then hook up the signal to the controller's cancelAll() slot.
For this to work, the pool must be declared after the workers list!
Suppose you destroy the controller (this). The following happens:
Body of this.~Controller executes.
At the end, the object is still an instance of Controller. Now it has to be torn down into an instance of the base class. The members will be destructed in reverse of the order of declaration.
threadPool is destroyed first. threadPool.~MyThreadPool emits aboutToWait.
this.cancelAll runs, since it's directly connected. The call originates in the moc-generated implementation of the signal method. It lets the workers know that they should stop.
Remainder of threadPool.~MyThreadPool runs, and members (if any) are destructed in reverse order of declaration. this is now an instance of QThreadPool.
threadPool.~QThreadPool runs and waits for the workers to stop. As each worker stops, the controller.onWorkerDone is invoked, since it's directly connected to relevant thread pool's signals. Eventually, threadPool.~QObject runs the pool is fully destructed.
workers.~QList runs.
this.~QObject runs.
The controller is fully destructed.

Qt "no matching function for call to connect", modifying Qt Fortune Threaded Server example

I am trying to modify the Qt Fortune Threaded Server example to read text from the connection and then echo it back. I defined tcpSocket in my FortuneThread.h file as follows:
QTcpSocket tcpSocket;
My new run function for the thread looks as follows:
void FortuneThread::run()
{
if (!tcpSocket.setSocketDescriptor(socketDescriptor)) {
emit error(tcpSocket.error());
return;
}
connect(&tcpSocket, SIGNAL(readyREAD()), this, SLOT(readCommand()) );
}
Which compiles and runs, but once I connect I get this error (referring to the connect line):
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QTcpSocket(0x1eeb920), parent's thread is QThread(0x1bb3f90), current thread is FortuneThread(0x1eeb8f0)
QObject::connect: No such signal QTcpSocket::readyREAD() in ../fortune/fortunethread.cpp:60
Can someone explain the cause to me? Since tcpSocket is defined inside the FortuneThread class (which is run as a separate thread), and "this" refers to FortuneThread, I assume both objects are inside the thread? How to fix this?
Your socket object has been created in the main thread, but you're accessing it from a different thread. You need to create it inside of the thread's run() method. The location where the socket is defined doesn't matter. It will be created from the main thread when the C++ runtime library is doing static object initialization.
QTcpSocket * tcpSocket;
...
void FortuneThread::run() {
tcpSocket = new QTcpSocket;
...
}
I agree with Kuba Ober. You should read that great guide about Qt threads, objects and events. In particular, section called Signals and slots across threads. Authors recommends split controller and work parts into different essences.
Second issue in your code — case sensitive signal name. Change it to readyRead.
A problem with the Qt Fortune Threaded Server example is the way in which it uses threads. As the developers of Qt say, "You're doing it wrong"
The issue is the inheritance of QThread. The QThread class is not actually a thread, but a thread controller class and the only reason to inherit this, is if you want to change the behaviour of controlling threads.
The problem you're seeing is due to thread affinity; which thread an object belongs to.
If a thread is inherited like this: -
class FortuneThread : public QThread
{
Q_OBJECT
private:
QTcpSocket tcpSocket;
};
An object of FortuneThread is then instantiated from the main thread: -
FortuneThread* ft = new FortuneThread(parent);
The thread affinity for the thread and the objects it has instantiated (tcpSocket) is now the main thread, so the tcpSocket is running on the main thread, which is what the error is stating. At the point the run function is called, the connect is coming from the FortuneThread, but the tcpSocket is on the main thread.
The better way to solve this is to create your class, derived from QObject and move it to the thread: -
// Inherit from QObject, not QThread
class FortuneSocket : public QObject
{
Q_OBJECT
public slots:
void Run();
private:
QTcpSocket tcpSocket;
};
QThread* pThread = new QThread(parent);
FortuneSocket* pFortune = new FortuneSocket(parent);
connect(pThread, &QThread::started, pFortune, &FortuneSocket::Run); // Qt5 connect style
// move the fortune socket to the thread: -
pFortune->moveToThread(pThread);
Now, when you start the thread with pThread->start(), the FortuneSocket object and all of its members are running on the new thread.
Using threads this way also means that you can move multiple objects to a single thread, rather than having one object per thread. Note that creating more threads than CPU cores is pointless!
Finally, there's a more in-depth article on how to use QThread, here.

QThread: Call child thread method from main thread

I have a thread created by inheriting QThread in which I called exec() to initiate the event loop. And this class that inherits QThread has a method in it.
How can I call that method from the main thread for it to execute in the child thread?
I assume that the execution of that method has to be queued in the child thread's event loop, so calling threadObject->childThreadMethod() won't be a good idea.
Is there any solution to this?
You can not call every member functions of the thread, but only slots and Q_INVOKABLE methods.
Use QMetaObject::invokeMethod() to call such a method, and make sure to set the connection type to Qt::QueuedConnection.
That way, the method will be called in the second thread whenever the control returns to the event loop in the second thread. The caller, i.e. the main thread, will continue immediately, so you won't be able to get a return value from the called method.
Behind the scenes, QMetaObject::invokeMethod adds a MetaCallEvent to the second thread's event queue.
Alternatively, create a signal/slot connection, and then emit a signal whenever you want the slot to be called in the other thread.
to run some function in a separate thread you can use QtConcurrent::run (i use it with QFutureWatcher). To run it every 5 or so seconds, use QElapsedTimer class
QFuture<void> future = QtConcurrent::run(this, &MyClass::foo2, ...foo2 arguments);
http://qt-project.org/doc/qt-4.8/qtconcurrentrun.html#run or check it here https://stackoverflow.com/search?q=QtConcurrent%3A%3Arun
or you can subclass QThread, reimplement QThread::run() method with the stuff you want to happen in your thread, and then create an instance of your thread and call start() on it.

Is there a way to detect when a QT QRunnable object is done?

Is there a way to detect when a QT QRunnable object is done? (Other than manually creating some signalling event at the end of the run() method.)
You can simply use QtConcurrent to run the runnable and use a QFuture to wait for finished.
#include <QtConcurrentRun>
class MyRunnable : public Runnable{
void run();
}
in order to run and wait for it you can do the following
//create a new MyRunnable to use
MyRunnable instance;
//run the instance asynchronously
QFuture<void> future = QtConcurrent::run(&instance, &MyRunnable::run);
//wait for the instance completion
future.waitForFinished();
Qtconcurrent::run will start a new thread to execute method run() on instance and immediately returns a QFuture that tracks instance progress.
Notice however that using this solution you are responsable to keep instance in memory during the execution.
There might be, or you might have to go a slight bit higher-level. The QFuture and QFutureWatcher classes are designed to work with Qt's Concurrent framework, and the QFutureWatcher class has a signal when the item it is watching has finished.
You can add your own SIGNAL which gets emitted as the last thing that your run() method does. Then simply connect it to coreesponding SLOT right before calling connect(...);

Resources