I am listening to a topic and want to display and update the received value every time it changes.
This function creates the logging part of the GUI
QGroupBox *Window::startLoggingGroup()
{
QGroupBox *groupBox = new QGroupBox(tr("Logging"));
log_value = new QPlainTextEdit;
log_value->setReadOnly(true);
log_value->setPlaceholderText("Value will appear here \n");
QHBoxLayout *hbox = new QHBoxLayout;
hbox->addWidget(log_carrot);
groupBox->setLayout(hbox);
return groupBox;
}
This is the code executed on every value changed.
void EFISWindow::callback_value(const geometry_msgs::PoseStamped& msg){
QString qst = QString::number(msg.pose.position.z);
log_value->setPlainText(qst);
}
It works at first, I can see the GUI and some values, but after several messages like the ones I show now it crashes:
QObject::connect: Cannot queue arguments of type 'QTextBlock' (Make
sure 'QTextBlock' is registered using qRegisterMetaType().) QObject:
Cannot create children for a parent that is in a different thread.
(Parent is QTextDocument(0x227e580), parent's thread is
QThread(0x1f9db50), current thread is QThread(0x7f4ae40011d0)
How can I solve this threading issue? Maybe using a signal-slot design? I don't really understand why this is not working.
You should not access a GUI element from another thread.
Maybe using a signal-slot design?
Yes, Your worker object should have a signal that you emit at some point and you should connect that signal to update the "log_value" value.
I have read various articles on the web relating to how to multithread applications in Qt such as the article here and I've noticed Qt have also updated their official documentation on the subject, however I am still struggling to understand how I can create a thread, do some image processing and return a new QImage to update my GUI.
The things I am struggling to get clarification on are:
Where do I put my connect code, in most examples I see the connections declared in the constructor of the object.
Why does a connect statement require so many lines to do one process? I.e. In my case I have a slider to change the saturation of an image on a QGraphicsView, I want to spawn a thread to handle the manipulation of images pixels and then return the formatted QPixmap to my GUI and run a render method to draw the new image to the canvas (I don't think I can update my canvas from my thread?)
Following point 2, here is the current code I have written for my thread (I am not subclassing QThread and I think I am following the documentation correctly.)
WorkerThread.h
#include "sliders.h"
class WorkerThread : public QObject
{
Q_OBJECT
public:
WorkerThread();
~WorkerThread();
public slots:
void modifySaturation(const int, const QPixmap);
signals:
void SaturationChanged(const QPixmap);
private:
Sliders *slider;
};
WorkerThread.cpp
WorkerThread::WorkerThread()
{
}
WorkerThread::~WorkerThread()
{
}
// Create a new Sliders object on the thread (declaring in construct would place it on the main thread?)
// Call the modifySaturation() method in the slider class and store its returned QPixmap into the variable to emit it back to the GUI
void WorkerThread::modifySaturation(const int value, const QPixmap image)
{
slider = new Sliders;
QPixmap img = slider->modifySaturation(value, image);
emit resultReady(img);
}
Hopefully the comments above convey what I wish to do in terms of emitting the newly created Pixmap back to main thread to draw to the GUI.
The step I am having troubles with is writing the logic to bridge the connection between my main thread and worker thread, so far I have created a QThread object called 'thread' in my mainwindow.h, then in my mainwindow.cpp I do the following:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
// Instanciate a scene object that we can draw to and then set up the ui
scene = new QGraphicsScene(this);
filter = new Filters;
worker = new WorkerThread;
ui->setupUi(this);
thread = new QThread;
worker->moveToThread(&thread);
// This is what I am struggling to understand
connect(thread, SIGNAL(started()), worker, SLOT(modifySaturation(int,QPixmap)));
connect(worker, SIGNAL(SaturationChanged(QPixmap)), MainWindow, SLOT(onSaturationChanged()));
}
// Public slot on my main window to update the GUI
void MainWindow::onSaturationChanged(QPixmap)
{
// image is a private member describing the current loaded image
m_image = QPixmap;
renderImageToCanvas();
}
From what I have read, I am supposed to spawn a new thread when I start a task, but how can I:
Reuse this thread for multiple methods (change saturation, change brightness, change hue...), do I need to create a new thread for every different task (this seems a bit over complicated)?
How can I connect the value changed method of my saturation slider to launch the computation on a new thread and then return it to update the GUI using the OnSaturationChanged slot in my main window?
As you've mentioned the great article How To Really Truly use QThread, let's start with explaining the code that you're unsure about by breaking this down
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();
This code will usually be placed in an object on the main thread, perhaps in the MainWindow: -
Create a new thread object - QThread is actually more of a thread controller than a thread
QThread* thread = new QThread;
Create a new worker object. This is an object which will do work on a different thread. As this can be moved to a different thread, note that you can create multiple objects and move them to the same thread
Worker* worker = new Worker();
Move the object and its children to the new thread
worker->moveToThread(thread);
Setup useful connections to monitor and control the worker. Let's start with any errors, so we know if the worker had a problem
connect(worker, SIGNAL(error(QString)), this, SLOT(errorString(QString)));
In order to start the worker object processing, we connect the started() signal of the thread to the process() slot of the worker. In your example, process would be the modifySaturation slot
connect(thread, SIGNAL(started()), worker, SLOT(process()));
When the worker has finished processing, if it is the only object, it needs to quit and clean up, so the thread should quit
connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
In order to tidy up, now both the worker and thread are no longer needed, make sure they tidy up after themselves
connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
Finally, let's get going by calling thread->start(), which will trigger the initial started() signal, which we previously connected to the worker's process() function
thread->start();
With all that in mind, let's tackle the questions posed: -
1 How can I reuse this thread for multiple methods (change saturation, change brightness, change hue...), do I need to create a new thread for every different task (this seems a bit over complicated)?
No, you do not need a new thread for each method. You can either use the current object and extend that to do all the processing, controlling it via signals and slots from the main thread, or you could create a separate object for each method and move them all to the new thread.
If you use multiple objects that are moved to the new thread, ensure that you don't connect an object's finished() signal to call quit() on the thread if other objects are still using that thread. However, you will still need to cleanup the objects and thread when you've finished with them.
2 Why does a connect statement require so many lines to do one process? i.e. in my case I have a slider to change the saturation of an image on a QGraphicsView, I want to spawn a thread to handle the manipulation of images pixels and then return the formatted QPixmap to my GUI and run a render method to draw the new image to the canvas (I don't think I can update my canvas from my thread?)
The general rule is that you can only update graphical objects (widgets, graphics items etc.) from the main thread. (There is an exception, but it's beyond the scope of this discussion and irrelevant here.)
When using just one object, of the multiple connect signals, three are used to delete the objects when finished, one for handling error messages and the final connect ensures that the worker object starts when the thread begins.
There is nothing stopping you changing this by creating your thread and starting it first, creating worker objects, connecting relevant signals and moving them to the thread afterwards, but you'd need to trigger the workers to start doing something, such as processing the saturation, once they've been moved to the new thread.
QThread* pThread = new QThread;
pThread->start();
Worker* worker1 = new Worker();
Worker* worker2 = new Worker();
Worker* worker3 = new Worker();
worker1->moveToThread(pThread);
worker2->moveToThread(pThread);
worker3->moveToThread(pThread);
The worker objects here have been moved to the new thread, which is running. However, the worker objects are idle. Without a connection, we can invoke a slot to be called. Let's assume that the process slot takes an integer parameter...
QMetaObject::invokeMethod( worker1, "process", Q_ARG( int, param ) );
QMetaObject::invokeMethod( worker2, "process", Q_ARG( int, param ) );
QMetaObject::invokeMethod( worker3, "process", Q_ARG( int, param ) );
So, as you see here, you don't always need to connect signals, but it is convenient.
I will answer one of your questions as Merlin covered the rest pretty well.
how can I connect the value changed method of my saturation slider to launch the computation on a new thread and then return it to update the GUI using the OnSaturationChanged slot in my main window?
Well for example, in your MainWindow::OnSaturationChanged slot you could emit a signal that passes the QImage and the slider value to your thread. This signal would be connected to a slot of your WorkerThread which does some image manipulation.
mainwindow.h
public slots:
void addNewImage(QImage image);
signals:
void requestImageUpdate(QImage image, int sliderValue);
mainwindow.cpp
//in your MainWindow constructor or wherever you create your worker...
connect(this, SIGNAL(requestImageUpdate(QImage, int)), worker, SLOT(updateImage(QImage, int)));
connect(worker, SIGNAL(imageUpdated(QImage)), this, SLOT(addNewImage(QImage)));
...
void MainWindow::OnSaturationChanged()
{
emit requestImageUpdate(myImage, slider->value());
}
void MainWindow::addNewImage(QImage image)
{
//update the image in your graphics view or do whatever you want to do with it
}
workerthread.h
public slots:
void updateImage(QImage image, int sliderValue);
signals:
void imageUpdated(QImage newImage);
workerthread.cpp
void WorkerThread::updateImage(QImage image, int sliderValue)
{
QImage newImage; // you might no need this, this is just an example
....
emit imageUpdated(newImage);
}
P.S. Use QPixmap only in the main thread. In other threads use QImage.
I have a Qt GUI class preferencesWindow that, obviously, is responsible for handling the user preferences. I have some fields that manage the connection to a database server. When a field is left, dbsChanged() method is called. Below is some code I managed to write:
void preferencesWindow::dbsChanged() {
QFuture<QStringList> loader = run(this, &preferencesWindow::get_databases);
QStringList databases = loader.result();
if (databases.length()) {
this->ui.database->show();
this->ui.nodb_label->hide();
this->ui.database->clear();
this->ui.database->addItems(databases);
this->ui.okButton->setDisabled(false);
this->ui.validationStatus->setPixmap(QPixmap(":/icon/tick.png"));
} else {
this->ui.database->hide();
this->ui.nodb_label->show();
this->ui.okButton->setDisabled(true);
this->ui.validationStatus->setPixmap(QPixmap(":/icon/error.png"));
}
}
QStringList preferencesWindow::get_databases() {
QSqlDatabase test_connection;
if (QSqlDatabase::contains("PREFEREMCES_LIVE_TEST_CONNECTION"))
test_connection = QSqlDatabase::database("PREFEREMCES_LIVE_TEST_CONNECTION");
else test_connection = QSqlDatabase::addDatabase("QMYSQL", "PREFEREMCES_LIVE_TEST_CONNECTION");
test_connection.setHostName(this->ui.serverAddress->text());
test_connection.setUserName(this->ui.username->text());
test_connection.setPassword(this->ui.password->text());
test_connection.setDatabaseName(this->ui.database->currentText());
test_connection.setPort(this->ui.serverPort->value());
test_connection.open();
qDebug() << "Error: " << test_connection.lastError();
QSqlQuery show_databases = test_connection.exec("show databases");
QStringList databases;
while (show_databases.next()) {
databases.append(show_databases.value(0).toString());
}
QSqlDatabase::removeDatabase("PREFERENCES_LIVE_TEST_CONNECTION");
return databases;
}
Since get_databases can take a long time, I thought that putting in on a separate thread as you can see in these two lines:
QFuture<QStringList> loader = run(this, &preferencesWindow::get_databases);
QStringList databases = loader.result();
could solve the problem. It runs on a separate thread, but it still freezes the GUI (while working).
How should I rewrite this entire process? I though of some solutions, but I am not really sure about their performance, and I don't want to work uselessly...
It freezes the GUI because even though the get_databases call is in a separate thread, you still wait for the results which causes the freeze.
I don't know how to do it in Qt, but the normal thing would be to open a dialog saying "please wait" or something with a cancel button, and have the worker thread send a signal to the parent (GUI) thread when done.
The QFuture will wait until the thread sets the result when your call loader.result(). You have to wait for that value later.
I guess you could store the future object as member of preferencesWindow and send yourself a signal, when finishing get_databases. So you give your application time to process other events during this wait time.
You can use QFutureWatcher to monitor that status of the QFuture object, like written in the documentation:
// Instantiate the objects and connect to the finished signal.
MyClass myObject;
QFutureWatcher<int> watcher;
connect(&watcher, SIGNAL(finished()), &myObject, SLOT(handleFinished()));
// Start the computation.
QFuture<int> future = QtConcurrent::run(...);
watcher.setFuture(future);
I've been trying that for a while and seems it's not something common as I didn't find much information about.
I have a QTree in which I put item, on my Item i have a check box on the first column.
Problem: Checkbox is not optimised to be treated as so and takes quite some time as soon as there is several elements.
So i'm using a thread to create the checkbox before putting in my list, but seems impossible to get the checkbox back on the GUI thread.
void CheckItemThread::run()
{
setPriority(QThread::IdlePriority);
QCheckBox *m_check;
m_check = new QCheckBox();
emit done(m_check);
}
My main thread:
myCheckItem::myCheckItem(QTreeWidget *parent, QStringList columnNames ):
myWidgetItem(parent)
{
m_parent = parent;
m_columnNames = columnNames;
connect(&TheThread,SIGNAL(done(QCheckBox *)), this, SLOT(retThread(QCheckBox *)));
connect(&TheThread,SIGNAL(terminated()), this, SLOT(endThread()));
TheThread.setdata(columnNames,parent, this);
TheThread.start(); //run thread
}
void myCheckItem::endThread()
{
m_check->setParent(m_parent);
connect(m_check, SIGNAL(stateChanged(int)), this, SLOT(onCheckBox(int)));
}
void myCheckItem::retThread(QCheckBox *check)
{
m_check = check;
}
Maybe I'm missing something or it's simple not possible to reattach the thread ?
Thanks
You must not create, edit or work with UI elements in other threads. UI elements must be worked with in the main thread (UI thread). If you have time-consuming prerequisites before "drawing" a checkbox, do your work in a thread (eventually QtConcurrent) and send a signal to the main thread for creating the corresponding checkbox.
You can change GUI elements only in main thread. How many checkboxes do you have? Maybe you should create a limited count of checkboxes and reuse them when needed?
There are two classes: Widget and Worker. Here is a schematic code.
class Widget
{
Worker *m_worker;
QTextEdit *m_edit;
public:
Widget():m_edit(new QTextEdit){}
~Widget()
{
m_worker->ShouldStop = true;
delete *m_worker;
}
void doWork()
{
m_worker = new Worker;
if (!worker->doWork())
m_edit->setText("failed");
}
}
class Worker
{
Worker() : ShouldStop(false){}
public:
bool ShouldStop;
bool doWork()
{
while(true && !ShouldStop)
{
QThread::sleep(1);
QApplication::processEvents();
}
//consider the work undone if a stop was forced
if (ShouldStop)
return false;
}
}
After a call to doWork() of the Widget the execution loops in the method doWork() of the Worker. A widget is then closed and its destructor is called during one of the calls to processEvents(). Then the execution then returns to the doWork() of the Worker.
It now checks ShouldStop and returns to the doWork() of the Widget and tries to add something to the m_edit. However the Widget object is already dead.
Questions:
How can a Worker be deleted cleanly?
What is the best design to avoid such an interplay?
Ideally, worker threads should just return data via the signals and slots mechanism, not directly accessing the original object. Actually creating a thread and avoiding the call to QApplication::processEvents() is the first way to avoid the problem.
Additionally, you should consider using a design like this when you want to start a worker in a new thread. Rather than subclassing QThread, you can just create a generic QThread and assign a QObject to it, like so:
Worker *worker = new Worker;
QThread *workerThread = new QThread(this);
connect(workerThread, &QThread::started, worker, &Worker::doWork);
connect(workerThread, &QThread::finished, worker, &Worker::deleteLater);
worker->moveToThread(workerThread);
// Starts an event loop, and emits workerThread->started()
workerThread->start();
Consider restructuring your code around this pattern if you're using Qt 5.
#Alex answer is definitely the way to go for q2. He just forgot to mention how to proceed for the deletion (q1). Assuming Widget created the object workerThread, then this is necessary:
~Widget(){
if(workerThread->isRunning()){
workerThread->terminate(); <--- this line, right there
workerThread->wait():
}
...
}
Why? Because deleting a QThread just delete the thread object but doesn't stop the thread from running, and may crash the application. Note that if you are using
connect(workerThread, &QThread::finished, worker, &Worker::deleteLater);
as Alex mentioned, then it is not safe to access worker after the call to terminate, because it will trigger the finished() signal ,execute worker->deleteLater, and eventually delete worker.
You could have Widget use a signal to inform the owner of Widget to start up Worker for the Widget.
Owner owns both Widget and Worker. Widget tells Owner that something was triggered, and Owner is responsible for deciding that Worker's doWork() is the correct response, and for managing the lifetime of both Worker and Widget. Worker and Widget are ignorant of each other, but Owner might connect some of their signals and slots together.