You are not logged in.

Dear visitor, welcome to QtForum.org. If this is your first visit here, please read the Help. It explains in detail how this page works. To use all features of this page, you should consider registering. Please use the registration form, to register here or read more information about the registration process. If you are already registered, please login here.

1

Sunday, November 21st 2010, 2:10pm

Starting a progress bar at the beginning of a function and stopping it at the end

I am trying to use a marquee progress bar to indicate that a long function is running.

I tried to do this:

Source code

1
2
3
4
5
6
7
8
void MainWindow::on_btnStart_clicked()
{
  this->ui.progressBar->show();

  // ... long code here ...

  this->ui.progressBar->hide();
}


but I noticed that the progress bar never got displayed. I made a simpler program to see what was going on:

Source code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
MainWindow::MainWindow()
{
  ui.setupUi(this);
  this->ui.progressBar->setMinimum(0);
  this->ui.progressBar->setMaximum(0);
  this->ui.progressBar->hide();
}

void MainWindow::on_btnStop_clicked()
{
  this->ui.progressBar->hide();
}

void MainWindow::on_btnStart_clicked()
{
  this->ui.progressBar->show();
}


I stepped through this code. What I notice is that the progress bar does not actually get shown when the show() function is called, but rather when the on_btnStart_clicked() function is exited. For my purposes, this doesn't help anything, because I hide() the progress bar at the end of the function!

Is there a way to do this?

Thanks,

David

2

Sunday, November 21st 2010, 5:38pm

I think you got into the ui lockup problem of all programming languages. If you run a long function on the same thread as the one that runs the ui, the ui locks. Multithreaded applications are the heaven and hell of programming. Read about it on the internet.

Qt specific, read about QThread here: http://doc.qt.nokia.com/4.7/qthread.html.

3

Sunday, November 21st 2010, 7:21pm

I tried to use a thread to start the progress bar, but it seems I can't touch the progress bar from another thread!

Here is a simple example:
form.h

Source code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#ifndef BUTTONFORM_H
#define BUTTONFORM_H

#include "ui_main.h"

#include <QThread>

class MainWindow : public QMainWindow, private Ui::MainWindow
{
    Q_OBJECT

public:
    MainWindow();

    class ProgressThread : public QThread
    {
    public:
      virtual void run();
      QProgressBar* ProgressBar;
    };

  public slots:

    void on_btnStart_clicked();

};

#endif


form.cpp

Source code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include "form.h"

#include <iostream>
#include <cmath>



void MainWindow::ProgressThread::run()
{
  this->ProgressBar->show();
}

MainWindow::MainWindow()
{
  this->setupUi(this);
  this->progressBar->setMinimum(0);
  this->progressBar->setMaximum(0);
  this->progressBar->hide();
}

void MainWindow::on_btnStart_clicked()
{
  std::cout << "Clicked." << std::endl;
  ProgressThread myThread;
  myThread.ProgressBar = this->progressBar;
  myThread.start();
  myThread.wait();
  
  for(unsigned int i = 0; i < 1e7; i++)
    {
    float a = sin(i);
    }
  this->progressBar->hide();
  std::cout << "Done." << std::endl;
}


The error is:

QCoreApplication::sendPostedEvents: Cannot send posted events for objects in another thread

Can anyone tell me how to change this so that it starts the progress bar as soon as the button is clicked?

Thanks,

David

4

Sunday, November 21st 2010, 8:11pm

You can not access objects between threads directly. In the docs there are a few ways of dealing with the problems, but i'll give you my choice to test (feel free to read more about it and chose the solution that fits you best).

What i do, is have a slot on my mainthread and a signal on my new made thread and connect them. In your case, define a custom signal and a slot. Connect them before starting the thread, then emit your custom signal instead of directly trying to modify the said object. One thing to wory about interthread signal/slot connection is that you need to use a 5th parameter, Qt::QueuedConnection, queueing the connection. Like this:

PHP Source code

1
connect(this,SIGNAL(my_signal),this,SLOT(my_slot),Qt::QueuedConnection);

5

Sunday, November 21st 2010, 9:54pm

I tried to do that but it won't let me add a signal to my thread object.

The error is:
/media/portable/Examples/c++/src/QT/ProgressBarThreaded/form.h:20: Error: Meta object features not supported for nested classes

Can you please take a look?

form.h

Source code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#ifndef BUTTONFORM_H
#define BUTTONFORM_H

#include "ui_main.h"

#include <QThread>

class MainWindow : public QMainWindow, private Ui::MainWindow
{
Q_OBJECT

public:
  MainWindow();

  class ProgressThread : public QThread
  {
  public:
    virtual void run();
    
  signals:
    void StartProgressSignal();
    void StopProgressSignal();
  };

  ProgressThread myProgressThread;

public slots:

  void on_btnStart_clicked();
  void ProgressSlot();

};

#endif


form.cpp

Source code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include "form.h"

#include <iostream>
#include <cmath>

void MainWindow::StartProgressSlot()
{
  this->progressBar->show();
}

void MainWindow::StopProgressSlot()
{
  this->progressBar->hide();
}

void MainWindow::ProgressThread::run()
{
  emit StartProgressSignal();
}

MainWindow::MainWindow()
{
  this->setupUi(this);
  this->progressBar->setMinimum(0);
  this->progressBar->setMaximum(0);
  this->progressBar->hide();

  connect(&myProgressThread, SIGNAL(StartProgressSignal()), this, SLOT(StartProgressSlot()), Qt::QueuedConnection);
  connect(&myProgressThread, SIGNAL(StopProgressSignal()), this, SLOT(StopProgressSlot()), Qt::QueuedConnection);
}

void MainWindow::on_btnStart_clicked()
{
  std::cout << "Clicked." << std::endl;
  
  myProgressThread.start();
  myProgressThread.wait();

  for(unsigned int i = 0; i < 1e7; i++)
    {
    float a = sin(i);
    }
  
  std::cout << "Done." << std::endl;
}


Thanks for your help so far!

David

6

Sunday, November 21st 2010, 10:14pm

Your mistake lies in nesting the classes. Nested classes can't have signals or slots. What does nesting mean? Defining a class inside another class. What to do? Define class ProgressThread : public QThread as a stand alone class (another cpp/h file). You should have no problems afterwards.

So just create a new instance of the progressthread using new, and ->run() it :D

7

Sunday, November 21st 2010, 10:24pm

Right right, I see. Before I had a nested class because I was trying to give it access to the ProgressBar widget, but that is not needed now that we are using your signal method.

It compiles now, but it doesn't work :( It still doesn't display the progress bar during the loop execution!

form.h

Source code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#ifndef BUTTONFORM_H
#define BUTTONFORM_H

#include "ui_main.h"

#include <QThread>

class ProgressThread : public QThread
{
Q_OBJECT
public:
  void run();
  void exit();

signals:
  void StartProgressSignal();
  void StopProgressSignal();
};
  
class MainWindow : public QMainWindow, private Ui::MainWindow
{
Q_OBJECT

public:
  MainWindow();

  ProgressThread myProgressThread;

public slots:

  void on_btnStart_clicked();
  void StartProgressSlot();
  void StopProgressSlot();

};

#endif


form.cpp

Source code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include "form.h"

#include <iostream>
#include <cmath>

void MainWindow::StartProgressSlot()
{
  this->progressBar->show();
}

void MainWindow::StopProgressSlot()
{
  this->progressBar->hide();
}

void ProgressThread::run()
{
  emit StartProgressSignal();
}

void ProgressThread::exit()
{
  emit StopProgressSignal();
}

MainWindow::MainWindow()
{
  this->setupUi(this);
  this->progressBar->setMinimum(0);
  this->progressBar->setMaximum(0);
  this->progressBar->hide();

  connect(&myProgressThread, SIGNAL(StartProgressSignal()), this, SLOT(StartProgressSlot()), Qt::QueuedConnection);
  connect(&myProgressThread, SIGNAL(StopProgressSignal()), this, SLOT(StopProgressSlot()), Qt::QueuedConnection);
}

void MainWindow::on_btnStart_clicked()
{
  std::cout << "Clicked." << std::endl;

  myProgressThread.start();

  for(unsigned int i = 0; i < 1e8; i++)
    {
    float a = sin(i);
    }

  std::cout << "Done." << std::endl;
  myProgressThread.exit();
}


Any thoughts? I think we're really close...

David

8

Sunday, November 21st 2010, 10:40pm

:D In the current form, you made the thread for nothing. The thread run() should hold the long execution function(s). Thus, move your for along with the .exit() to void ProgressThread::run(). This body of code is the one to be executed in another thread.

9

Sunday, November 21st 2010, 10:46pm

Hm, but I would like to run the for loop in the main thread and the progress bar in the second thread. This way the existing structure of my code doesn't have to change nearly as much (and I could almost turn "on and off" the progress bar functionality). Is this possible?

10

Sunday, November 21st 2010, 10:54pm

The main thread holds the ui. If you run long calculating functions in this thread it will lock up the ui. I dunno if you can create widgets in some threads that have parents in other threads, so i would say creating the window in a thread and the progressbar in another is not doable, and not even good programming practice. All ui is inside a main thread, long calculating functions in threads that pop and disappear as needed.

11

Sunday, November 21st 2010, 11:19pm

I see what you're saying. Here is the working code for the next reader:

(form.h is unmodified from my previous posting)

form.cpp

Source code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include "form.h"

#include <iostream>
#include <cmath>

void MainWindow::StartProgressSlot()
{
  this->progressBar->show();
}

void MainWindow::StopProgressSlot()
{
  this->progressBar->hide();
}

void ProgressThread::run()
{
  emit StartProgressSignal();
  for(unsigned int i = 0; i < 1e8; i++)
    {
    float a = sin(i);
    }

  std::cout << "Done." << std::endl;
  exit();
  emit StopProgressSignal();
}

void ProgressThread::exit()
{
  emit StopProgressSignal();
}

MainWindow::MainWindow()
{
  this->setupUi(this);
  this->progressBar->setMinimum(0);
  this->progressBar->setMaximum(0);
  this->progressBar->hide();

  connect(&myProgressThread, SIGNAL(StartProgressSignal()), this, SLOT(StartProgressSlot()), Qt::QueuedConnection);
  connect(&myProgressThread, SIGNAL(StopProgressSignal()), this, SLOT(StopProgressSlot()), Qt::QueuedConnection);
}

void MainWindow::on_btnStart_clicked()
{
  std::cout << "Clicked start." << std::endl;
  myProgressThread.start();
}


I'm having trouble though introducing this into my actual program. I have this setup:

Source code

1
2
3
4
5
6
7
8
9
10
// this is a function of the main Qt form which does all of the work
void Form::btnCut_clicked()
{

// .. get some settings from the Qt interface ...

this->MyClass->PerformSegmentation();

// .. set the output of the segmentation on the Qt form ...
}


I gave my ProgressThread object a pointer to the MyClass object and called PerformSegmentation from the run() function:

Source code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class CProgressThread : public QThread
{
Q_OBJECT
public:
  void run();
  void exit();

  ImageGraphCutBase* MyClass;
  
signals:
  void StartProgressSignal();
  void StopProgressSignal();
};
....

void CProgressThread::run()
{
  emit StartProgressSignal();

  this->MyClass->PerformSegmentation();
  
  exit();
  emit StopProgressSignal();
}


and the new btnCut_clicked() function looks like this:

Source code

1
2
3
4
5
6
7
8
9
10
11
// this is a function of the main Qt form which does all of the work
void Form::btnCut_clicked()
{

// .. get some settings from the Qt interface ...

this->ProgressThread.GraphCut = this->GraphCut;
ProgressThread.start();

// .. set the output of the segmentation on the Qt form ...
}


The progress bar works great! It starts when I click the button and stops when the function finishes. However, the output is empty, but it works fine when I just call this->MyClass->PerformSegmentation(); from the main thread. Do you have an idea what could be going wrong?

David

12

Sunday, November 21st 2010, 11:25pm

sorry but i have no idea what ImageGraphCutBase or PerformSegmentation() are. Anyway, it has something to do with the PerformSegmentation() function. It probably tries to access some data from the objects on the other thread and fails. Would be nice to see some code.

13

Sunday, November 21st 2010, 11:34pm

Unfortunately the code is thousands of lines so I can't post it :(

Wouldn't it crash or complain if it was trying to access information from the other thread?

I noticed this -

Source code

1
2
3
4
5
6
7
8
void Form::btnCut_clicked()
{
 // ... get inputs from Form ...
  this->ProgressThread.GraphCut = this->GraphCut;
  ProgressThread.start();
  //ProgressThread.wait(); // with this commented, the output is blank. With this active, the code works properly, but the progress bar doesn't ever turn on!

 // ... set outputs on Form ...


Does that give you any hints? I think the problem was that the second thread was starting and the outputs were then immediately trying to be set (before the second thread finished, until which the output is not valid).

See what I mean? I think I just must not be using wait() correctly.

David

14

Monday, November 22nd 2010, 12:09am

Well according to the docs, wait just blocks the thread, unless some stuff occurs. So with it you only see the progressbar, and without it you only see the output? AAAAh i think it got it!

Your function sets the output on the form as soon as it starts the thread. the output must be on another slot function, connected to an emit signal when the segmentation finishes.

15

Monday, November 22nd 2010, 12:27am

Brilliant! It works! It's unfortunate that it requires so much restructuring, but it is a cool functionality.

Thanks so much for your help! I don't see a method of giving "kudos" on this forum - if there is one please point me to it - you definitely deserve some!

David