call functions using QtConcurrent/QFuture
-
What's the difference when I put the ui caller directly in the thread loop without using emit, will it cause an error one day?
ui->label->setText("Value : " + QString::number(Counter)); //emit Updategui_signal("Value : " + QString::number(Counter));
mainwindow.h
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QtConcurrent> #include <QDebug> #include <QThread> QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); signals: void Updategui_signal(QString data); private slots: void Updategui_slot(QString data); void on_pushButton_clicked(); private: Ui::MainWindow *ui; int Counter = 0; bool isThread_start = false; bool isThread_running = false; QFuture<void> myThread; void ConcurrentThread(); }; #endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h" #include "ui_mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); connect(this, SIGNAL(Updategui_signal(QString)),this,SLOT(Updategui_slot(QString))); } MainWindow::~MainWindow() { delete ui; } void MainWindow::Updategui_slot(QString data) { ui->label->setText(data); } void MainWindow::ConcurrentThread() { while (isThread_running == true) { QThread::msleep(100); Counter = Counter + 1; // call function GUI ui->label->setText("Value : " + QString::number(Counter)); //emit Updategui_signal("Value : " + QString::number(Counter)); } } void MainWindow::on_pushButton_clicked() { if (isThread_start == false) { isThread_start = true; isThread_running = true; myThread = QtConcurrent::run(this,&MainWindow::ConcurrentThread); myThread.resume(); } else { isThread_start = false; isThread_running = false; myThread.pause(); myThread.cancel(); } }
-
@Blackzero said in call functions using QtConcurrent/QFuture:
What's the difference when I put the ui caller directly in the thread loop without using emit, will it cause an error one day?
ui->label->setText("Value : " + QString::number(Counter)); //emit Updategui_signal("Value : " + QString::number(Counter));
Don't access the GUI directly from another thread.
Using a signal to send/receive notifications or get/set values is better.connect(this, SIGNAL(Updategui_signal(QString)),this,SLOT(Updategui_slot(QString)));
Do yourself a favor and learn/use the function pointer based connection style.
Edit:
Btw. your code includes a lot of redundant and unnecessary stuff...
myThread = QtConcurrent::run(this,&MainWindow::ConcurrentThread); myThread.resume(); // does not work with QtConcurrent::run
Resumes the asynchronous computation represented by the future(). This is a convenience method that simply calls setSuspended(false).
(https://doc.qt.io/qt-6/qfuture.html#resume)And:
Be aware that not all computations can be suspended. For example, the QFuture returned by QtConcurrent::run() cannot be suspended; but the QFuture returned by QtConcurrent::mappedReduced() can.
( https://doc.qt.io/qt-6/qfuture.html#setSuspended)bool isThread_start = false; bool isThread_running = false;
One of them also not needed since you use them for the same purpose and set both of them
true
/false
at the same time.
And since you cannot pause yourQtConcurrent::run
anyway, there is no point of having two boolean variables to check whether your task was started and if it's running or was stopped/paused.
Even if it would work or is needed in your case, you still have these two to get the state of yourQFuture
thread at any time:bool QFuture::isRunning() const
Returns true if the asynchronous computation represented by this future is currently running; otherwise returns false.
bool QFuture::isStarted() const
Returns true if the asynchronous computation represented by this future has been started; otherwise returns false.
-
@Blackzero
As my personal experience don't dare to update GUI control Directly in therad and never block the thread by update the control in thread.you will get may kind of problem when you update GUI control Directly in therad like GUI Frezzing, control may not update sometime, also you will lost the stylesheet of your control in some cases.
@Pl45m4 said in call functions using QtConcurrent/QFuture:
Using a signal to send/receive notifications or get/set values is better.
this is the correct way to update GUI control by using signal and slot functionality.
-
@Blackzero said in call functions using QtConcurrent/QFuture:
ui->label->setText("Value : " + QString::number(Counter));
This will crash eventually because it is not running inside the GUI thread. What you could do instead is
QMetaObject::invokeMethod(qApp, [this]() { ui->label->setText("Value:" + QString::number(Counter); });
. Having a signal instead to call is much better. The signal will automatically detect that the slot is located in a different thread and will dispatch it to the other thread accordingly. Using signals/slots makes multi-threading a lot easier.If you really want to wait for 100ms you should use a timer. In a lot of cases this is even sufficient and you'll not need a separate thread for that. If you really need a thread (because you want to do a lot more stuff in addition what you have showed), just start a plain old QThread. It will have its own event queue running and thus can serve QTimers. Usually, you should use worker objects in this context. However, you also need to start the timer inside the thread. So, I personally would just use lambdas without a worker object:
QMetaObject::invokeMethod(QAbstractEventDispatcher::instance(thread), [this]() { m_timer = new QTimer(m_thread); QObject::connect(m_timer, &QTimer::timeout, QAbstractEventDispatcher::instance(thread), [this]() { Counter = Counter + 1; QMetaObject::invokeMethod(qApp, [this]() { ui->label->setText("Value:" + QString::number(Counter); }); }); m_timer->start(100); }
QMetaObject::invokeMethod is like manually placing a slot call into an event queue. Notice that
connect
needs a context object inside the correct thread (maybe it would even work without the context object because the timeout will happen inside the correct thread).