简体   繁体   中英

Qt5.15: how to access widget object from a QtConcurrent function

I wish to update a progress bar widget from a QtConcurrent function and stuck on the following problem:

a) If I declare this function as:

void myRunFunction(QString str)

then I successfully program it as concurrent by:

QFuture<void> t1 = QtConcurrent::run(myRunFunction, QString("A"));

BUT I cannot access to any Qt widget of my GUI from inside the function ("unable to resolve identifier 'widget' ").

b) If I declare this function as:

void mainForm::myRunFunction(QString str)

then I successfully access my widgets inside it BUT cannot longer program it as concurrent getting the compiler error:

error: invalid use of non-static member function ‘void mainForm::myRunFunction(QString)’

at line:

QFuture<void> t1 = QtConcurrent::run(myRunFunction, QString("A"));

How can I solve the problem? Many thanks in advance, Marco

In Qt all widgets should live in the main GUI thread. All other threads shouldn't directly access widgets from the main thread, Qt doesn't guarantee thread safety here. What is the solution? To use Qt's builtin queued mechanisms. There are two methods.

  1. If there is a QObject derived class in your second thread, you can use a Qt::QueuedConnection Qt::signal/slot connection. You can also use Qt::AutoConnection, which is default, but I prefer to explicitly state what I need.

From documentation:

(Qt::AutoConnection) If the receiver lives in the thread that emits the signal, Qt::DirectConnection is used. Otherwise, Qt::QueuedConnection is used. The connection type is determined when the signal is emitted.

  1. If you do not have any QObjects in your second thread - use QMetaObject::invokeMethod.

How to provide the caller with the pointer to callee? I would recommend using a closure (a lambda with capture).

But be careful about lifetimes of your objects. It is your responsibility now to check that captured pointer to the widget points to the valid widget longer than the lifetime of non-GUI thread.

According to your code, the second variant is suited better for you. There is a small example:

// guiwidget.h
class GuiWidget : public QWidget
{
    Q_OBJECT

public:
    GuiWidget(QWidget *parent = nullptr);
    ~GuiWidget() {};

   // public function for variant 2
   void function(int data) {
      // update widget
   }

// slot for variant 1
public slots:
    void function_slot(int data) {
        // update widget
    }
};

And somewhere in you.cpp file:

GuiWidget *widget = new GuiWidget(this);
// declare a lambda
auto f = [widget] (QString str) 
{
    for (int i = 0; i < str.toInt(); ++i) {
        // do some job
        // ...
        // job is done
        // send progress data to mainwidget
        QMetaObject::invokeMethod(widget, [widget, i] () 
        {
            widget->function(i);
        }, Qt::QueuedConnection);
    }
};

auto t1 = QtConcurrent::run(f, "100");

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM