0

我希望从 QtConcurrent 函数更新进度条小部件并遇到以下问题:

a) 如果我将此函数声明为:

void myRunFunction(QString str)

然后我通过以下方式成功地将其编程为并发:

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

但是我无法从函数内部访问我的 GUI 的任何 Qt 小部件(“无法解析标识符'widget'”)。

b)如果我将此函数声明为:

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"));

我该如何解决这个问题?非常感谢,马可

4

1 回答 1

0

在 Qt 中,所有小部件都应该存在于主 GUI 线程中。所有其他线程不应该直接从主线程访问小部件,Qt 在这里不保证线程安全。解决办法是什么?使用 Qt 的内置队列机制。有两种方法。

  1. 如果第二个线程中有 QObject 派生类,则可以使用 Qt::QueuedConnection Qt::signal/slot 连接。您也可以使用默认的 Qt::AutoConnection,但我更喜欢明确说明我需要什么。

从文档:

(Qt::AutoConnection) 如果接收者位于发出信号的线程中,则使用 Qt::DirectConnection。否则,使用 Qt::QueuedConnection。连接类型在信号发出时确定。

  1. 如果您的第二个线程中没有任何 QObject - 使用 QMetaObject::invokeMethod。

如何为调用者提供指向被调用者的指针?我建议使用闭包(带有捕获的 lambda)。

但要小心你的对象的生命周期。您现在有责任检查捕获的指向小部件的指针指向的有效小部件是否比非 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");
于 2020-08-06T14:33:07.693 回答