Qt5.15: как получить доступ к объекту виджета из функции QtConcurrent

Я хочу обновить виджет индикатора выполнения из функции QtConcurrent и застрял на следующей проблеме:

а) Если я объявлю эту функцию как:

void myRunFunction(QString str)

затем я успешно запрограммировал его как параллельный:

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

НО я не могу получить доступ к какому-либо виджету Qt моего графического интерфейса изнутри функции ("невозможно разрешить идентификатор 'виджет' ").

б) Если я объявлю эту функцию как:

void mainForm::myRunFunction(QString str)

то я успешно получаю доступ к своим виджетам внутри него, НО не могу больше программировать его как одновременное получение ошибки компилятора:

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

в строке:

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

Как я могу решить проблему? Заранее большое спасибо, Марко

1 ответ

В Qt все виджеты должны находиться в основном потоке графического интерфейса. Все другие потоки не должны напрямую обращаться к виджетам из основного потока, Qt здесь не гарантирует безопасность потоков. Каково решение? Чтобы использовать встроенные механизмы Qt с очередями. Есть два метода.

  1. Если во втором потоке есть производный класс QObject, вы можете использовать соединение Qt::QueuedConnection Qt::signal/slot. Вы также можете использовать Qt::AutoConnection, который установлен по умолчанию, но я предпочитаю явно указывать, что мне нужно.

Из документации:

(Qt::AutoConnection) Если получатель живет в потоке, излучающем сигнал, используется Qt:: DirectConnection. В противном случае используется Qt:: QueuedConnection. Тип подключения определяется при передаче сигнала.

  1. Если у вас нет QObject во втором потоке - используйте QMetaObject::invokeMethod.

Как предоставить вызывающей стороне указатель на вызываемого? Я бы рекомендовал использовать замыкание (лямбда с захватом).

Но будьте осторожны со сроками жизни ваших объектов. Теперь вы обязаны проверить, что захваченный указатель на виджет указывает на действительный виджет дольше, чем время жизни потока без графического интерфейса.

Согласно вашему коду, вам больше подходит второй вариант. Вот небольшой пример:

// 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
    }
};

И где-то в вашем.cpp файле:

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");
Другие вопросы по тегам