0

I want a QProgressBar to show in a modal dialog and update asynchronously as another thread does work.

    struct ProgressThread : public QThread
    {
        QDialog m_dialog;
        QProgressBar * m_bar;

        ProgressThread (QWidget * parent, int range)
        : m_dialog (parent)
        , m_bar (new QProgressBar ())
        {
            auto l = new QGridLayout (& m_dialog);

            l -> addWidget (m_bar, 0, 0);

            m_bar -> setRange (0, range);
        }

        void run () override
        {
            m_dialog .exec ();
        }
    };

    ProgressThread thread (this, range);

    thread .start ();

    int num = 0;

    for (each job)
    {
            do_some_work ();

            thread .m_bar -> setValue (++ num);
    }

    thread .m_dialog .accept ();
    thread .wait (1000);
    thread .quit ();

It basically works except the progress bar renders as a black block. Why is this happening?

spraff
  • 32,570
  • 22
  • 121
  • 229
  • It also render black if I do `(new QProgressBar ())->show();` in the main thread, and paints only after the work block completes. – spraff Nov 27 '15 at 21:59

1 Answers1

1

This is a design problem. Neither Qt nor most of other UI frameworks designed to run multiple threads for UI though this innovative approach may take place. Most of UI classes just expect to be running on same main thread. Qt can never guarantee that it works the way you are attempting: running the dialog exec() on a worker thread.

How to convert the source code you have to Qt way? Your source code encapsulates QDialog and QProgressBar immediately in thread instance which is maybe not incorrect yet until you do moveToThread(uiObject). But still, it shows the wrong design. You can just follow normal pattern for the worker thread interacting with UI:

// make sure to place it in the header file
class ProgressThread : public QDialog
{
    Q_OBJECT
    public:
       // if you ever need to access that from outside
       // QProgressBar* bar() {return m_bar;}
    public slots:
       void moveProgress(int);
    private:
       QProgressBar* m_bar;
};

class ProgressThread : public QThread
{
    Q_OBJECT
    public:
       // add methods
    signals:
       moveProgress(int);
};

You need to connect the slot to signal:

// for interthread communication a queued connection will be automatical
// figure out how to get the pointer to that dialog class (dialogPtr)
QObject::connect(&thread, SIGNAL(moveProgress(int)), dialogPtr(), SLOT(moveProgress(int));

And from the thread code you emit the signal to advance the progress bar on UI thread:

 emit moveProgress(value);

The similar question/answers: QProgressBar not showing progress?

And make sure that UI thread starts from the application main() function!

Community
  • 1
  • 1
Alexander V
  • 8,351
  • 4
  • 38
  • 47