Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Unable to run a function on a concurrent thread.
QtWS25 Last Chance

Unable to run a function on a concurrent thread.

Scheduled Pinned Locked Moved Solved General and Desktop
concurrencythreadsmultithreadgifqt 6.3.0
9 Posts 2 Posters 1.2k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • B Offline
    B Offline
    BigBen
    wrote on last edited by
    #1

    I have a class named myClass. It has a slot on_button_clicked(). There is a separate function called devKit_funct() which is not a part of this myClass.

    I want to run devKit_funct() when the button is clicked. In other words, I want to run devKit_funct() in the on_button_clicked() function. I am able to do this successfully and achieve my tasks. However, this devKit_funct() takes a few seconds to complete. In the time it takes to complete, I want to execute a function which loads a gif in a label and has a loading icon video. This function is called loading().

    loading() works fine independently, however when I run loading() inside on_button_clicked() followed by devKit_funct(), the loading() function does not run at all, and only the devKit_funct() runs.

    So, I guessed that I need to use a different thread to execute the loading() function while the devKit_funct() is being executed, so that they both run simultaneously.

    Given this, my program became the following, when I attempted implementing this:

    myClass.h -

    
     class myClass : public QDialog
    {
        Q_OBJECT
    
    public:
         myClass(QWidget *parent = nullptr);
        void loading();
    
    private slots:
        void on_button_clicked();
    
    private:
        Ui::myClass *ui;
        QMovie *movie;
        int a, b;
    };
    

    myClass.cpp -

    myClass::myClass(QWidget *parent) :
        QDialog(parent),
        ui(new Ui::myClass)
    {
        ui->setupUi(this);
        a=1;
        b=1;
    }
    
    void myClass::on_button_clicked(){
        QFuture<void> future = QtConcurrent::run([this] () -> void { return loading(); });
        if(devKit_funct(a, b)==32)
        {
            qDebug()<<"Error in devkit_funct()\n";
            return;
        }
    }
    
    void myClass::loading{
        movie= new QMovie(":/picture/images/watermelonLoad.gif");
        ui->loadLabel->setMovie(movie);
        ui->loadLabel->show();
        movie->start();
    }
    
    

    However, even with this, I am not able to see the loading gif when I click the button. It still only executes the devKit_funct(), and after it is done executing, I see just a flicker/glimpse of the loading icon, and then it disappears.

    I don't understand what I am doing wrong here. Would be grateful for some guidance.

    JonBJ 1 Reply Last reply
    0
    • B BigBen

      I have a class named myClass. It has a slot on_button_clicked(). There is a separate function called devKit_funct() which is not a part of this myClass.

      I want to run devKit_funct() when the button is clicked. In other words, I want to run devKit_funct() in the on_button_clicked() function. I am able to do this successfully and achieve my tasks. However, this devKit_funct() takes a few seconds to complete. In the time it takes to complete, I want to execute a function which loads a gif in a label and has a loading icon video. This function is called loading().

      loading() works fine independently, however when I run loading() inside on_button_clicked() followed by devKit_funct(), the loading() function does not run at all, and only the devKit_funct() runs.

      So, I guessed that I need to use a different thread to execute the loading() function while the devKit_funct() is being executed, so that they both run simultaneously.

      Given this, my program became the following, when I attempted implementing this:

      myClass.h -

      
       class myClass : public QDialog
      {
          Q_OBJECT
      
      public:
           myClass(QWidget *parent = nullptr);
          void loading();
      
      private slots:
          void on_button_clicked();
      
      private:
          Ui::myClass *ui;
          QMovie *movie;
          int a, b;
      };
      

      myClass.cpp -

      myClass::myClass(QWidget *parent) :
          QDialog(parent),
          ui(new Ui::myClass)
      {
          ui->setupUi(this);
          a=1;
          b=1;
      }
      
      void myClass::on_button_clicked(){
          QFuture<void> future = QtConcurrent::run([this] () -> void { return loading(); });
          if(devKit_funct(a, b)==32)
          {
              qDebug()<<"Error in devkit_funct()\n";
              return;
          }
      }
      
      void myClass::loading{
          movie= new QMovie(":/picture/images/watermelonLoad.gif");
          ui->loadLabel->setMovie(movie);
          ui->loadLabel->show();
          movie->start();
      }
      
      

      However, even with this, I am not able to see the loading gif when I click the button. It still only executes the devKit_funct(), and after it is done executing, I see just a flicker/glimpse of the loading icon, and then it disappears.

      I don't understand what I am doing wrong here. Would be grateful for some guidance.

      JonBJ Offline
      JonBJ Offline
      JonB
      wrote on last edited by JonB
      #2

      @BigBen said in Unable to run a function on a concurrent thread.:

      So, I guessed that I need to use a different thread to execute the loading() function while the devKit_funct() is being executed, so that they both run simultaneously.

      Qt requires all UI operations to run only in the main thread.

      However, this devKit_funct() takes a few seconds to complete. In the time it takes to complete,

      You could do computation work in another thread, that is fine. But show your movie in main UI thread.

      1 Reply Last reply
      0
      • B Offline
        B Offline
        BigBen
        wrote on last edited by
        #3

        @JonB Thank you for the reply and for the information. I get your point.
        Can you show me how what you said translates into code based on my class structure? Thanks

        JonBJ 1 Reply Last reply
        0
        • B BigBen

          @JonB Thank you for the reply and for the information. I get your point.
          Can you show me how what you said translates into code based on my class structure? Thanks

          JonBJ Offline
          JonBJ Offline
          JonB
          wrote on last edited by
          #4

          @BigBen

          1. Only call loading(), which does the QMovie, from the main thread. Do that immediately before:
          2. Use QtConcurrent::run(), or other thread worker, to do what time-consuming, computation, non-UI stuff you need to do.
          3. When the thread finishes, stop the movie.
          1 Reply Last reply
          0
          • B Offline
            B Offline
            BigBen
            wrote on last edited by BigBen
            #5

            @JonB

            I tried the below code, but here, the devKit_funct() gets executed first, and when it is done, the movie starts playing. There is still something that I am doing wrong.

            myClass::myClass(QWidget *parent) :
                QDialog(parent),
                ui(new Ui::myClass)
            {
                ui->setupUi(this);
                a=1;
                b=1;
            }
            
            void myClass::on_button_clicked(){
                loading()
                QFuture<void> future = QtConcurrent::run([this] () -> int { return devKit_funct(a, b); });
                if(future.result()==32)
                {
                    qDebug()<<"Error in devkit_funct()\n";
                    return;
                }
            }
            
            void myClass::loading{
                movie= new QMovie(":/picture/images/watermelonLoad.gif");
                ui->loadLabel->setMovie(movie);
                ui->loadLabel->show();
                movie->start();
            }
            
            JonBJ 1 Reply Last reply
            0
            • B BigBen

              @JonB

              I tried the below code, but here, the devKit_funct() gets executed first, and when it is done, the movie starts playing. There is still something that I am doing wrong.

              myClass::myClass(QWidget *parent) :
                  QDialog(parent),
                  ui(new Ui::myClass)
              {
                  ui->setupUi(this);
                  a=1;
                  b=1;
              }
              
              void myClass::on_button_clicked(){
                  loading()
                  QFuture<void> future = QtConcurrent::run([this] () -> int { return devKit_funct(a, b); });
                  if(future.result()==32)
                  {
                      qDebug()<<"Error in devkit_funct()\n";
                      return;
                  }
              }
              
              void myClass::loading{
                  movie= new QMovie(":/picture/images/watermelonLoad.gif");
                  ui->loadLabel->setMovie(movie);
                  ui->loadLabel->show();
                  movie->start();
              }
              
              JonBJ Offline
              JonBJ Offline
              JonB
              wrote on last edited by JonB
              #6

              @BigBen said in Unable to run a function on a concurrent thread.:

              if(future.result()==32)

              As soon as you ask for QFuture.result() your program blocks at this point, waiting for the thread to finish and return a result:

              If a result is not available at the time of calling the result(), resultAt(), or results() functions, QFuture will wait until the result becomes available.

              You must not do this until the QFuture signals that it has finished:

              To interact with running tasks using signals and slots, use QFutureWatcher.

              You will need to create a QFutureWatcher and it will send you a QFutureWatcher::resultReadyAt() signal when the thread has finished, see https://doc.qt.io/qt-5/qfuturewatcher.html#details. Your QFuture<void> future & QFutureWatcher variables will need to be class member variables, to persist from thread start to finish.

              1 Reply Last reply
              1
              • B Offline
                B Offline
                BigBen
                wrote on last edited by
                #7

                @JonB Thank you for the clarification. I understand what you're saying, but having slight difficulty in converting it to code. Need some guidance filling up some gaps in the code below.
                --------- In the code are areas that I want to fill or want to replace with appropriate code, but not able to understand how to.

                myClass.h -

                class myClass : public QDialog
                {
                    Q_OBJECT
                
                public:
                    myClass(QWidget *parent = nullptr);
                    void loading();
                    QFuture<int> res;
                    QFutureWatcher<int> futWatch;
                
                private slots:
                    void on_button_clicked();
                
                private:
                    Ui::myClass *ui;
                    QMovie *movie;
                    int a, b;
                };
                

                myClass.cpp -

                myClass::myClass(QWidget *parent) :
                    QDialog(parent),
                    ui(new Ui::myClass)
                {
                    ui->setupUi(this);
                    a=1;
                    b=1;
                }
                
                void myClass::on_button_clicked(){
                    loading();
                    res= QtConcurrent::run([this] ()->int {return devKit_funct(a, b);});
                    futWatch.setFuture(res);
                    QObject::connect(futWatch, SIGNAL(resultReadyAt(int)), --------,---------);
                    
                    if(devKit_funct(a, b)==32   -------------)
                    {
                        qDebug()<<"Error in devkit_funct()\n";
                        return;
                    }
                }
                
                void myClass::loading{
                    movie= new QMovie(":/picture/images/watermelonLoad.gif");
                    ui->loadLabel->setMovie(movie);
                    ui->loadLabel->show();
                    movie->start();
                }
                
                JonBJ 1 Reply Last reply
                0
                • B BigBen

                  @JonB Thank you for the clarification. I understand what you're saying, but having slight difficulty in converting it to code. Need some guidance filling up some gaps in the code below.
                  --------- In the code are areas that I want to fill or want to replace with appropriate code, but not able to understand how to.

                  myClass.h -

                  class myClass : public QDialog
                  {
                      Q_OBJECT
                  
                  public:
                      myClass(QWidget *parent = nullptr);
                      void loading();
                      QFuture<int> res;
                      QFutureWatcher<int> futWatch;
                  
                  private slots:
                      void on_button_clicked();
                  
                  private:
                      Ui::myClass *ui;
                      QMovie *movie;
                      int a, b;
                  };
                  

                  myClass.cpp -

                  myClass::myClass(QWidget *parent) :
                      QDialog(parent),
                      ui(new Ui::myClass)
                  {
                      ui->setupUi(this);
                      a=1;
                      b=1;
                  }
                  
                  void myClass::on_button_clicked(){
                      loading();
                      res= QtConcurrent::run([this] ()->int {return devKit_funct(a, b);});
                      futWatch.setFuture(res);
                      QObject::connect(futWatch, SIGNAL(resultReadyAt(int)), --------,---------);
                      
                      if(devKit_funct(a, b)==32   -------------)
                      {
                          qDebug()<<"Error in devkit_funct()\n";
                          return;
                      }
                  }
                  
                  void myClass::loading{
                      movie= new QMovie(":/picture/images/watermelonLoad.gif");
                      ui->loadLabel->setMovie(movie);
                      ui->loadLabel->show();
                      movie->start();
                  }
                  
                  JonBJ Offline
                  JonBJ Offline
                  JonB
                  wrote on last edited by JonB
                  #8

                  @BigBen
                  Bearing in mind that I have never used QFuture/QFutureWatcher...:

                  QObject::connect(futWatch, &QFutureWatcher::finished, this, &myClass::onFinished);
                  
                  void myClass::onFinished()
                  {
                      move->stop();
                  }
                  

                  if(devKit_funct(a, b)==32 -------------)

                  No idea what you want to do here. This line executes (in your main UI thread) immediately after you have set off the

                  res= QtConcurrent::run([this] ()->int {return devKit_funct(a, b);});
                  

                  Guessing that you want to do something with the result (return devKit_funct(a, b);), get rid of the if(devKit_funct(a, b)==32 ... in on_button_clicked(), you must move it to the myClass::onFinished() slot which be called when the thread finishes, e.g.

                  void myClass::onFinished()
                  {
                      move->stop();
                      int result =  futWatch.result();
                      qDebug() << "devKit_funct(a, b) returned:" << result;
                      if (result == 32)
                      {
                          qDebug()<<"Error in devkit_funct()\n";
                          return;
                      }
                  }
                  
                  B 1 Reply Last reply
                  1
                  • JonBJ JonB

                    @BigBen
                    Bearing in mind that I have never used QFuture/QFutureWatcher...:

                    QObject::connect(futWatch, &QFutureWatcher::finished, this, &myClass::onFinished);
                    
                    void myClass::onFinished()
                    {
                        move->stop();
                    }
                    

                    if(devKit_funct(a, b)==32 -------------)

                    No idea what you want to do here. This line executes (in your main UI thread) immediately after you have set off the

                    res= QtConcurrent::run([this] ()->int {return devKit_funct(a, b);});
                    

                    Guessing that you want to do something with the result (return devKit_funct(a, b);), get rid of the if(devKit_funct(a, b)==32 ... in on_button_clicked(), you must move it to the myClass::onFinished() slot which be called when the thread finishes, e.g.

                    void myClass::onFinished()
                    {
                        move->stop();
                        int result =  futWatch.result();
                        qDebug() << "devKit_funct(a, b) returned:" << result;
                        if (result == 32)
                        {
                            qDebug()<<"Error in devkit_funct()\n";
                            return;
                        }
                    }
                    
                    B Offline
                    B Offline
                    BigBen
                    wrote on last edited by
                    #9

                    @JonB

                    Thank you for your guidance.
                    I was able to solve this issue with your help.

                    For others facing this issue:

                    The connect() function that worked for me was:

                    QObject::connect(&futWatch, &QFutureWatcher<int>::finished, this, &myClass::onFinished);

                    And the onFinished() function was the same as suggested by Jon

                    1 Reply Last reply
                    0

                    • Login

                    • Login or register to search.
                    • First post
                      Last post
                    0
                    • Categories
                    • Recent
                    • Tags
                    • Popular
                    • Users
                    • Groups
                    • Search
                    • Get Qt Extensions
                    • Unsolved