2016-07-22 5 views
1

Ich versuche, QtConcurrent :: run zu verwenden, um eine Funktion innerhalb meiner MainWindow-Klasse auszuführen damit die UI während der Berechnungen weiterhin reagiert. Hier ist, wie ich es umgesetzt:QtConcurrent :: run with MainWindow-Funktion, Warnmeldung "QObject :: setParent: Übergeordnetes Element nicht festlegen, neues übergeordnetes Element ist in einem anderen Thread"

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(); 
    } 

Es funktioniert gut, und die Benutzeroberfläche reagiert und der Fortschrittsbalken wird korrekt aktualisiert, jedoch in der Ausgabe erhalte ich die Fehlermeldung „QObject :: setParent: Kann nicht eingestellt Eltern, neue Mutter ist in einem anderen Thread "oft, von etwas, das in einer Schleife ausgeführt wird.

Irgendwelche Ideen, was das verursacht, oder Vorschläge für eine bessere Implementierung von QtConcurrent :: run?

Dank

+1

Das Problem besteht darin, dass Sie die Benutzeroberfläche von Threads außer dem Hauptthread aktualisieren. Siehe [Dokumentation] (http://doc.qt.io/qt-5/threads-technologies.html#example-use-cases), Führen Sie die Funktion mit QtConcurrent :: run() aus, * Schreiben Sie an einen Thread- Sichere Variable zum Aktualisieren des Fortschritts *. – Mike

+1

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

+0

perfect thanks guys –

Antwort

1

Sie keine Qt bereitgestellten QWidget Objekte aus dem Arbeitsthread werden kann berühren, da die meisten ihrer Methoden Thread-sicher nicht.

Stattdessen können Sie die Berechnungen im Worker durchführen und dann Funktoren übergeben, die den Status im Hauptthread aktualisieren. Einzelheiten finden Sie unter this answer.

Ihr Code wäre dann:

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

die calculateIntegral in ähnlicher Art und Weise ändern, aber stellen Sie sicher, dass Sie nicht emittieren Fortschritte zu oft aktualisiert.

Stellen Sie außerdem sicher, dass auf die Mitglieder, die Sie von dem Worker aktualisieren, nicht anderswo durch den UI-Code zugegriffen wird. Dies kann schwierig sein, da Sie UI und Berechnungen mischen. Stattdessen abstrahieren Sie den Mitarbeiter zu einem QObject, der keine Benutzeroberfläche hat, und verbinden ihn mit anderem Code über Signale, die Fortschritt/Status anzeigen. Sie werden weiterhin QtConcurrent::run innerhalb dieses Objekts verwenden, es wird jedoch einfacher sicherzustellen, dass keine anderen Threads auf den privaten Status dieses Objekts zugreifen.

Um fertige Ergebnisse aus dem Arbeiter Funktor zu schieben, können Sie ein Signal aussenden, das die Ergebnisse trägt. Der Typ Data sollte billig zu kopieren sein, z.B. Sie können es unter Verwendung QSharedData/QSharedDataPointer implementieren. Oder Sie können es über eine QSharedPointer halten.

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

Sie können auch die Ergebnisse im Objekt speichern und achten, dass sie nicht zugegriffen werden, während die Berechnungen aktiv sind:

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; 
    } 
}; 

Natürlich, wenn Sie speichern den Verweis auf data() in der Hauptthread und dann compute() anrufen, haben Sie undefiniertes Verhalten, also tun Sie das nicht.

Wenn eines der Datentypen eine implizit gemeinsamen Behälter, wie QVector oder QString, können Sie sie als Wert zurückgeben sollte, und jeder Zugriff wird Thread-sicher sein:

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

Beachten Sie, dass QFile ist eine richtige C++ - Klasse. Es gibt alle Ressourcen frei, wenn es zerstört wird. Das manuelle Schließen der Datei ist unnötig: der Compiler soll Ihnen hier helfen, das ist der Sinn des C++ - Objektmodells im Vergleich zu z. Java's.

Verwandte Themen