2

I have a program which has class MainWindow that inherited from QMainWindow. I want to run a specific function in a new thread.

Since I can't inherit from QThread so the best solution is to use QtConcurrent. I stored QtConcurrent result in a QFuture but as mentioned in the QT documentation, the result of QtConcurrent can not be canceled by QFuture::cancel.

I see some questions like mine but the answers are to create a class which inherit from QThread that I can't. So what is the solution? Here is my codes:

mainwindow.h:

using namespace  std;

class MainWindow : public QMainWindow
{
    Q_OBJECT


public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
    ...


private:
    Ui::MainWindow *ui;
    ...
    QFuture<void> future;
    QFutureWatcher< void > futureWatcher;
    QPushButton *stop_pushButton_=new QPushButton;

private slots:
    ...
    void on_graph_pushButton_clicked();
    void generateGraph();
    void stopPushButtonClicked();
    };

mainwindow.cpp:

void MainWindow::stopPushButtonClicked()
{
    futureWatcher.setFuture(future);
    futureWatcher.cancel();
    futureWatcher.waitForFinished();
}

   ...
void MainWindow::on_graph_pushButton_clicked()
{
    loadingOn();
    future = QtConcurrent::run(this,&MainWindow::generateGraph);
}

void MainWindow::generateGraph()
{
    InsertLogs::getInstance()->printStatus(USER_GENERATE_GRAPH);
    GenGraph* gr=new GenGraph;
    connect(this,&MainWindow::loadingOffSignal,this,&MainWindow::loadingOff);
    graphType_=gr->generateGraph(persons_comboBox_->currentText(),
                      ui->graphResource1_comboBox->currentText(),
                      ui->graphResource2_comboBox->currentText(),
                      ui->graphType_comboBox->currentText());

    InsertLogs::getInstance()->printSuccess(SYSTEM_GENERATE_GRAPH);

    emit loadingOffSignal();
}

When I click on graph_pushButton, generatedGraph is called in a new thread, but when I click on stop_pushButton the future does not stop and the generatedGraph function executes completely.

  • I supose, your `generateGraph` function has some kind of loop, as long as it has long execution time and you want to call it in separate thread. So you can use `bool abortGraphGenerating` flag, and use `break` to exit the loop. – Bearded Beaver Jan 15 '18 at 10:22
  • thanks, no it runs another function from an external library which has long execution time and i can't change the library function by adding a flag... –  Jan 15 '18 at 10:26
  • Why not create a wrapper class that inherits both your MainWindow as well as QThread , to call that function. – Mohammad Kanan Jan 15 '18 at 10:31
  • Please read the doc about `QThread`, inheriting from the class is **not** the good practice to run something in another thread ! When it comes to thread you must first think about the data that will be read/written and split it accordingly into different classes. – ymoreau Jan 15 '18 at 10:34
  • 1
    MohammadKanan thanks, it may work, but is there not a simpler way? –  Jan 15 '18 at 10:35
  • @ymoreau thanks, is there another way to use QThread without inheriting? –  Jan 15 '18 at 10:40
  • @mortezaaliahmadi there is QtConcurrent where you just execute a function (but you must be cautious about the shared data), or to move a worker QObject to thread and then execute the slots in that thread, see the doc : https://doc.qt.io/qt-5/qthread.html#details – ymoreau Jan 15 '18 at 10:43
  • @morteza ali ahmadi, may be you can achieve your goal of `cancel` by implementing QFutureWatcher and event loop. – Mohammad Kanan Jan 15 '18 at 10:51
  • @ymoreau of cource, my function just creates a graph and saves it on disk, so I'm not worried about losing data. –  Jan 15 '18 at 10:51
  • @MohammadKanan can QFutureWatcher stop the thread of a Future? I think it can't –  Jan 15 '18 at 10:54
  • @morteza ali ahmadi, my understanding is yes it has `cancel` slot that can be connected to, `void QFutureWatcher::cancel() Cancels the asynchronous computation represented by the future(). Note that the cancelation is asynchronous. Use waitForFinished() after calling cancel() when you need synchronous cancelation.` – Mohammad Kanan Jan 15 '18 at 11:02
  • @MohammadKanan thanks again, does it means i have to add these commands to my stop function? `futureWatcher.setFuture(future);` `futureWatcher.cancel();` `futureWatcher.waitForFinished();` –  Jan 15 '18 at 11:08
  • @morteza ali ahmadi , depends upon your implementation but generally Yes. – Mohammad Kanan Jan 15 '18 at 11:10
  • @MohammadKanan i tested before, but it still does not work and the function executes completely... –  Jan 15 '18 at 11:14
  • @morteza ali ahmadi, you need to update your question then , with the exact code and indicate your expectation from the code – Mohammad Kanan Jan 15 '18 at 11:18
  • @MohammadKanan thanks, ok –  Jan 15 '18 at 11:26
  • @MohammadKanan I add my codes yesterday. Can you help me? –  Jan 16 '18 at 10:19
  • @morteza ali ahmadi , the QFuture returned by `QtConcurrent::run()` cannot be canceled; but the QFuture returned by `QtConcurrent::mappedReduced()` can. – Mohammad Kanan Jan 16 '18 at 16:09
  • @MohammadKanan but does `QtConcurrent::mappedReduced()` execute a arbitrary function in a new thread? –  Jan 17 '18 at 05:05
  • @morteza ali ahmadi, Honestly this function is new to me, I dont know its limits .. you better check. – Mohammad Kanan Jan 17 '18 at 10:42
  • @MohammadKanan thanks you, but I think that just `run` function is done in a new thread... –  Jan 17 '18 at 19:22

0 Answers0