gpt4 book ai didi

c++ - QtConcurrent::run with MainWindow 函数,警告消息 "QObject::setParent: Cannot set parent, new parent is in a different thread"

转载 作者:行者123 更新时间:2023-11-30 05:24:58 25 4
gpt4 key购买 nike

我正在尝试使用 QtConcurrent::run 在我的 MainWindow 类中执行一个函数,以便 UI 在计算期间保持响应。以下是我的实现方式:

    void MainWindow::on_calculate_pb_clicked()
{
QFuture<void> future = QtConcurrent::run(this,&MainWindow::calculation);
}

void MainWindow::calculation()
{
progressBar->show();
loadMap();
integral.clear();
positions.clear();
offset.clear();
lines = 0;
for(int i=0;i<paths.size();i++)
{
if(i ==0)
{
lines = countNumberOfLines(paths.at(i));
}
double file = i+1;
ui->statusBar->showMessage(QString("Processing file %1 of %2").arg(file).arg(paths.size()));

calculateIntegral(paths.at(i));
offset.push_back(ui->tableWidget->item(i,1)->text().toDouble());
}
makePositionVector();

plotData(ui->customPlot);
ui->export_pb->setEnabled(true);
progressBar->hide();
ui->statusBar->showMessage("Done",3000);

}

void MainWindow::calculateIntegral(QString path)
{
QVector<double> mappeddata,tempdata;
mappeddata.resize(map.size());
tempdata.resize(numberofdetectors);

double currentLine = 0;

QFile File(path);
if(File.exists()==true){
File.open(QIODevice::ReadOnly);
QTextStream in(&File);
double val;

while(!in.atEnd())
{
for(int j = 0;j<numberofdetectors;j++)
{
in >> val;
tempdata[j]+=val;
currentLine++;
double progress = currentLine/lines*100;
progressBar->setValue(progress);
}

}

for(int i =0;i<map.size();i++)
{
mappeddata[i] = tempdata.at(map.at(i));
}
for(int k = 0;k<numberofdetectors; k++)
{
integral.push_back(mappeddata.at(k));
}

}
File.close();
}

它工作正常,用户界面响应迅速,进度条更新正确,但是在输出中我多次收到错误“QObject::setParent:无法设置父级,新父级在不同的线程中”循环执行。

知道是什么原因造成的,或者对更好地实现 QtConcurrent::run 有什么建议吗?

谢谢

最佳答案

您不能从工作线程中接触任何 Qt 提供的 QWidget 对象,因为它们的大多数方法都不是线程安全的。

相反,解决此问题的一种方法是在 worker 中进行计算,然后将更新状态的仿函数提交到主线程。参见 this answer了解详情。

您的代码将变为:

void MainWindow::calculation()
{
postToThread([this]{ progressBar->show(); });
loadMap();
integral.clear();
positions.clear();
offset.clear();
lines = 0;
for(int i=0;i<paths.size();i++)
{
if (i == 0)
lines = countNumberOfLines(paths.at(i));
auto file = i+1;
postToThread([this]{
ui->statusBar->showMessage(
tr("Processing file %1 of %2").arg(file).arg(paths.size()));
});
calculateIntegral(paths.at(i));
postToThread([this]{
offset.push_back(ui->tableWidget->item(i,1)->text().toDouble());
});
}
makePositionVector();

postToThread([this]{
plotData(ui->customPlot);
ui->export_pb->setEnabled(true);
progressBar->hide();
ui->statusBar->showMessage("Done",3000);
});
}

以类似的方式修改 calculateIntegral,但请确保您不会过于频繁地发出进度更新。

还要确保 UI 代码不会在其他地方访问您从 worker 更新的成员。这可能很难,因为您混合了 UI 和计算。相反,将 worker 抽象为没有 UI 的 QObject,并通过指示进度/状态的信号将其连接到其他代码。您仍将在该对象中使用 QtConcurrent::run,但确保没有其他线程访问该对象的私有(private)状态变得更简单。

要将完成的结果从 worker 仿函数中推出,您可以发出一个包含结果的信号。 Data 类型的复制成本应该很低,例如您可以使用 QSharedData/QSharedDataPointer 来实现它。或者您可以通过 QSharedPointer 保存它。

class Computation : public QObject {
Q_OBJECT
void work() {
Data data;
... // updates data
emit finished(data);
}
public:
Q_SLOT void compute() {
QtConcurrent::run(&Worker::work, this);
}
Q_SIGNAL void finished(Data data);
};

您还可以将结果存储在对象中,并注意在计算处于事件状态时不会访问它们:

class Computation : public QObject {
Q_OBJECT
bool m_active { false };
Data m_data;
void work() {
... // updates m_data
m_active = false;
}
public:
Q_SLOT void compute() {
m_active = true;
QtConcurrent::run(&Worker::work, this);
}
const Data & data() const {
Q_ASSERT(! m_active);
return m_data;
}
};

当然,如果您在主线程中存储对 data() 的引用,然后调用 compute(),您将有未定义的行为,所以不要不要那样做。

如果任何数据类型是隐式共享容器,如 QVectorQString,您应该按值返回它们,并且任何访问都是线程安全的:

  QVector<MyData> data() const {
Q_ASSERT(! m_active);
return m_data;
}

请注意,QFile 是一个正确的 C++ 类。当它被破坏时,它会释放持有的任何资源。手动关闭文件是不必要的:编译器应该在这里帮助你,这就是 C++ 对象模型与例如Java 的。

关于c++ - QtConcurrent::run with MainWindow 函数,警告消息 "QObject::setParent: Cannot set parent, new parent is in a different thread",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38523509/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com