2016-07-22 10 views
1

Я пытаюсь использовать QtConcurrent :: run для выполнения функции в моем классе MainWindow, так что пользовательский интерфейс остается гибким во время вычислений. Вот как я реализовал его:QtConcurrent :: run с функцией MainWindow, предупреждающее сообщение «QObject :: setParent: невозможно установить родительский, новый родитель в другом потоке»

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?

Благодаря

+1

проблема в том, что вы обновляете пользовательский интерфейс из потоков, отличных от основного потока. См. [Документация] (http://doc.qt.io/qt-5/threads-technologies.html#example-use-cases). Запустите эту функцию, используя QtConcurrent :: run(), * Write to thread- безопасная переменная для обновления прогресса *. – Mike

+1

https://forum.qt.io/topic/44153/access-to-components-of-ui-from-the-function-of-qtconcurrent-run-foo/2 – Marco

+0

безупречное спасибо, ребята –

ответ

1

Вы не можете прикасаться к Qt-предоставленные QWidget объектов из рабочего потока, так как большинство их методов не потокобезопасные.

Вместо этого, чтобы решить это, нужно выполнить вычисления в рабочем месте, а затем представить функторы, которые обновляют состояние до основного потока. См. 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 аналогичным образом, но убедитесь, что вы не испускают обновляет прогресс слишком часто.

Также убедитесь, что члены, которые вы обновляете у работника, не обращаются в другом месте с помощью кода пользовательского интерфейса. Это может быть сложно, так как вы смешиваете пользовательский интерфейс и вычисления. Вместо этого отвлечь рабочего на QObject, который не имеет пользовательского интерфейса, и связать его с другим кодом с помощью сигналов, указывающих на прогресс/статус. Вы по-прежнему будете использовать QtConcurrent::run внутри этого объекта, но становится проще гарантировать, что никакие другие потоки не получат доступ к частному состоянию этого объекта.

Чтобы вывести законченные результаты из рабочего функтора, вы можете испустить сигнал, который несет результаты. Тип 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(), у вас будет неопределенное поведение, поэтому не делайте этого.

Если какой-либо из типов данных неявно общий контейнер, как QVector или QString, вы должны вернуть их по значению, и любой доступ будет потокобезопасной:

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

Обратите внимание, что QFile является надлежащим классом C++. Он освобождает любые ресурсы, удерживаемые при его разрушении. Закрытие файла вручную не требуется: компилятор должен помочь вам здесь, это целая точка объектной модели C++ по сравнению с, например, В Java.