Qt5.15: как получить доступ к объекту виджета из функции QtConcurrent - PullRequest
0 голосов
/ 06 августа 2020

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

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

void myRunFunction(QString str)

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

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

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

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

void mainForm::myRunFunction(QString str)

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

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

at строка:

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

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

1 Ответ

0 голосов
/ 06 августа 2020

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

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

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

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

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

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

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

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

// 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");
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...