我想从QtConcurrent函数更新进度条小部件,但遇到了以下问题:
a( 如果我将此函数声明为:
void myRunFunction(QString str)
然后我成功地将其编程为并发:
QFuture<void> t1 = QtConcurrent::run(myRunFunction, QString("A"));
但我无法从函数内部访问GUI的任何Qt窗口小部件("无法解析标识符‘窗口小部件’"(。
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"));
我该如何解决这个问题?非常感谢,Marco
在Qt中,所有小部件都应该位于主GUI线程中。所有其他线程都不应该直接从主线程访问小部件,Qt在这里不能保证线程安全。解决方案是什么?使用Qt的内置排队机制。有两种方法。
- 如果第二个线程中有一个QObject派生类,则可以使用Qt::QueuedConnection Qt::signal/slot连接。您也可以使用Qt::AutoConnection,这是默认的,但我更喜欢明确地说明我需要什么
来自文档:
(Qt::AutoConnection(如果接收器位于发出信号的线程中,使用Qt::DirectConnection。否则,将使用Qt::QueuedConnection。连接类型是在发出信号时确定的。
- 如果您的第二个线程中没有任何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");