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. Problems with stacked Progress Dialogs
Forum Updated to NodeBB v4.3 + New Features

Problems with stacked Progress Dialogs

Scheduled Pinned Locked Moved Unsolved General and Desktop
12 Posts 6 Posters 460 Views 3 Watching
  • 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.
  • J JanLaloux

    Qt 6.9.0 on Windows11, with C++ in Visual Studio

    I have a program that runs different search algorithms in sequence. I want to show a global progress indicator that shows the number of executed steps, and for each step a separate indicator that shows progress of execution of that particular algorithm. I included a small runnable program at the bottom that shows the setup.

    These are the problems:

    1. The progress dialog window is initially completely blank for some time
    2. The step progress dialog is show before the global progress dialog
    3. The setMinimumDuration value seems to be ignored for the step progress dialogs
    4. Once a step dialog has been shown, it is not possible to cancel the global dialog anymore

    Is something wrong in my setup? Thanks for your assistance!

    #include "stdafx.h"
    #include <windows.h>
    #include <thread>
    #include <QtCore/QPoint>
    #include <QtWidgets/QApplication>
    #include <QtWidgets/QMainWindow>
    #include <QtWidgets/QPushButton>
    #include <QtWidgets/QProgressDialog>
    
    QMainWindow* mainWindow;
    QProgressDialog* progressStep = nullptr;
    QProgressDialog* progressGlobal = nullptr;
    
    bool progressStepIsCanceled;
    bool progressGlobalIsCanceled;
    
    void progressStepCancel() {
    	progressStepIsCanceled = true;
    }
    void progressGlobalCancel() {
    	progressGlobalIsCanceled = true;
    }
    
    inline bool progressStepCanceled() {
    	if( not progressStep )
    		return false;
    	return progressStepIsCanceled or progressGlobalIsCanceled;
    }
    inline bool progressGlobalCanceled() {
    	if( not progressGlobal )
    		return false;
    	return progressGlobalIsCanceled;
    }
    
    inline void progressStepProgress( const int CurrentStep ) {
    	if( not progressStep )
    		return;
    	progressStep->setValue( CurrentStep );
    }
    inline void progressGlobalProgress( const int CurrentStep ) {
    	if( not progressGlobal )
    		return;
    	progressGlobal->setValue( CurrentStep );
    }
    
    void progressStepClose() {
    	if( not progressStep )
    		return;
    
    	progressStep->setValue( progressStep->maximum() );
    	progressStep->close();
    	progressStep = nullptr;
    }
    void progressGlobalClose() {
    	// Also cancel the strategy progress window, if any
    	progressStepClose();
    
    	if( not progressGlobal )
    		return;
    
    	progressGlobal->setValue( progressGlobal->maximum() );
    	progressGlobal->close();
    	progressGlobal = nullptr;
    }
    
    
    void RunStep() {
    
    	progressStep = new QProgressDialog( "Step progress", "Cancel", 0, 5, mainWindow );
    	progressStep->setWindowModality( Qt::WindowModal );
    	QPoint Qp{ mainWindow->pos() };
    	QPoint Incr( 100, 100 );
    	progressStep->move( Qp + Incr );
    	progressStep->setMinimumDuration( 1500 );
    	QObject::connect( progressStep, &QProgressDialog::canceled, mainWindow, &progressStepCancel );
    	progressStep->setValue( 0 );
    	progressStepIsCanceled = false;
    
    	for( int si = 0; si <= 5; si++ ) {
    		progressStepProgress( si );
    		std::this_thread::sleep_for( std::chrono::seconds( 1 ) );
    
    		if( progressStepCanceled() )
    			break;
    	}
    	progressStepClose();
    }
    
    void RunGlobal() {
    
    	progressGlobal = new QProgressDialog( "Global progress", "Cancel", 0, 10, mainWindow );
    	progressGlobal->setWindowModality( Qt::WindowModal );
    	QPoint Qp{ mainWindow->pos() };
    	QPoint Incr( 50, 50 );
    	progressGlobal->move( Qp + Incr );
    	progressGlobal->setMinimumDuration( 500 );
    	QObject::connect( progressGlobal, &QProgressDialog::canceled, mainWindow, &progressGlobalCancel );
    	progressGlobal->setValue( 0 );
    	progressGlobalIsCanceled = false;
    
    	std::this_thread::sleep_for( std::chrono::seconds( 2 ) );
    
    	for( int gi = 0; gi <= 20; gi++ ) {
    		progressGlobalProgress( gi );
    		std::this_thread::sleep_for( std::chrono::seconds( 2 ) );
    
    		RunStep();
    
    		if( progressGlobalCanceled() )
    			break;
    	}
    	progressGlobalClose();
    }
    
    int APIENTRY _tWinMain( _In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, _In_ int nCmdShow ){
    	int argc = 0;
    	char Argv[1][100];
    	char* argv = Argv[0];
    
    	QApplication* Application = new QApplication( argc, &argv );
    	mainWindow = new QMainWindow;
    
    	QPushButton* Start = new QPushButton( mainWindow );
    	Start->setText( "Start" );
    	QObject::connect( Start, &QPushButton::clicked, mainWindow, &RunGlobal );
    
    	mainWindow->show();
    	Application->exec();
    	return 0;
    }
    
    Pl45m4P Offline
    Pl45m4P Offline
    Pl45m4
    wrote last edited by
    #2

    @JanLaloux said in Problems with stacked Progress Dialogs:

    Is something wrong in my setup?

    Is the code above just an example or your real code?
    Why you use winMain? Qt already does that for you when you are on Windows and create a new Qt Widget App.


    If debugging is the process of removing software bugs, then programming must be the process of putting them in.

    ~E. W. Dijkstra

    S 1 Reply Last reply
    0
    • J Offline
      J Offline
      JanLaloux
      wrote last edited by
      #3

      This is copy of real code. I do not use Qt Creator, straight C++ code

      1 Reply Last reply
      0
      • Christian EhrlicherC Offline
        Christian EhrlicherC Offline
        Christian Ehrlicher
        Lifetime Qt Champion
        wrote last edited by
        #4

        What has winmain to do with QtCreator? Simply use main()

        Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
        Visit the Qt Academy at https://academy.qt.io/catalog

        J 1 Reply Last reply
        0
        • J JanLaloux

          Qt 6.9.0 on Windows11, with C++ in Visual Studio

          I have a program that runs different search algorithms in sequence. I want to show a global progress indicator that shows the number of executed steps, and for each step a separate indicator that shows progress of execution of that particular algorithm. I included a small runnable program at the bottom that shows the setup.

          These are the problems:

          1. The progress dialog window is initially completely blank for some time
          2. The step progress dialog is show before the global progress dialog
          3. The setMinimumDuration value seems to be ignored for the step progress dialogs
          4. Once a step dialog has been shown, it is not possible to cancel the global dialog anymore

          Is something wrong in my setup? Thanks for your assistance!

          #include "stdafx.h"
          #include <windows.h>
          #include <thread>
          #include <QtCore/QPoint>
          #include <QtWidgets/QApplication>
          #include <QtWidgets/QMainWindow>
          #include <QtWidgets/QPushButton>
          #include <QtWidgets/QProgressDialog>
          
          QMainWindow* mainWindow;
          QProgressDialog* progressStep = nullptr;
          QProgressDialog* progressGlobal = nullptr;
          
          bool progressStepIsCanceled;
          bool progressGlobalIsCanceled;
          
          void progressStepCancel() {
          	progressStepIsCanceled = true;
          }
          void progressGlobalCancel() {
          	progressGlobalIsCanceled = true;
          }
          
          inline bool progressStepCanceled() {
          	if( not progressStep )
          		return false;
          	return progressStepIsCanceled or progressGlobalIsCanceled;
          }
          inline bool progressGlobalCanceled() {
          	if( not progressGlobal )
          		return false;
          	return progressGlobalIsCanceled;
          }
          
          inline void progressStepProgress( const int CurrentStep ) {
          	if( not progressStep )
          		return;
          	progressStep->setValue( CurrentStep );
          }
          inline void progressGlobalProgress( const int CurrentStep ) {
          	if( not progressGlobal )
          		return;
          	progressGlobal->setValue( CurrentStep );
          }
          
          void progressStepClose() {
          	if( not progressStep )
          		return;
          
          	progressStep->setValue( progressStep->maximum() );
          	progressStep->close();
          	progressStep = nullptr;
          }
          void progressGlobalClose() {
          	// Also cancel the strategy progress window, if any
          	progressStepClose();
          
          	if( not progressGlobal )
          		return;
          
          	progressGlobal->setValue( progressGlobal->maximum() );
          	progressGlobal->close();
          	progressGlobal = nullptr;
          }
          
          
          void RunStep() {
          
          	progressStep = new QProgressDialog( "Step progress", "Cancel", 0, 5, mainWindow );
          	progressStep->setWindowModality( Qt::WindowModal );
          	QPoint Qp{ mainWindow->pos() };
          	QPoint Incr( 100, 100 );
          	progressStep->move( Qp + Incr );
          	progressStep->setMinimumDuration( 1500 );
          	QObject::connect( progressStep, &QProgressDialog::canceled, mainWindow, &progressStepCancel );
          	progressStep->setValue( 0 );
          	progressStepIsCanceled = false;
          
          	for( int si = 0; si <= 5; si++ ) {
          		progressStepProgress( si );
          		std::this_thread::sleep_for( std::chrono::seconds( 1 ) );
          
          		if( progressStepCanceled() )
          			break;
          	}
          	progressStepClose();
          }
          
          void RunGlobal() {
          
          	progressGlobal = new QProgressDialog( "Global progress", "Cancel", 0, 10, mainWindow );
          	progressGlobal->setWindowModality( Qt::WindowModal );
          	QPoint Qp{ mainWindow->pos() };
          	QPoint Incr( 50, 50 );
          	progressGlobal->move( Qp + Incr );
          	progressGlobal->setMinimumDuration( 500 );
          	QObject::connect( progressGlobal, &QProgressDialog::canceled, mainWindow, &progressGlobalCancel );
          	progressGlobal->setValue( 0 );
          	progressGlobalIsCanceled = false;
          
          	std::this_thread::sleep_for( std::chrono::seconds( 2 ) );
          
          	for( int gi = 0; gi <= 20; gi++ ) {
          		progressGlobalProgress( gi );
          		std::this_thread::sleep_for( std::chrono::seconds( 2 ) );
          
          		RunStep();
          
          		if( progressGlobalCanceled() )
          			break;
          	}
          	progressGlobalClose();
          }
          
          int APIENTRY _tWinMain( _In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, _In_ int nCmdShow ){
          	int argc = 0;
          	char Argv[1][100];
          	char* argv = Argv[0];
          
          	QApplication* Application = new QApplication( argc, &argv );
          	mainWindow = new QMainWindow;
          
          	QPushButton* Start = new QPushButton( mainWindow );
          	Start->setText( "Start" );
          	QObject::connect( Start, &QPushButton::clicked, mainWindow, &RunGlobal );
          
          	mainWindow->show();
          	Application->exec();
          	return 0;
          }
          
          jeremy_kJ Online
          jeremy_kJ Online
          jeremy_k
          wrote last edited by
          #5

          @JanLaloux said in Problems with stacked Progress Dialogs:

            std::this_thread::sleep_for( std::chrono::seconds( 1 ) );
          

          I only glanced through the code (TL;DR), but this is a terrible thing to do in a GUI or other interactive program. The UI will be completely unresponsive to user input and graphic updates during the sleep period. Use a QTimer and a slot for the next step if the UI must wait for some period of time before continuing.

          Asking a question about code? http://eel.is/iso-c++/testcase/

          1 Reply Last reply
          1
          • J Offline
            J Offline
            JanLaloux
            wrote last edited by
            #6

            The sleep is just for the demo program of course, it replaces the processing that happens in the real program. I could write a for loop with some dummy calculations, but that does not change fundamentally the setup: a main loop that launches one step after the other in sequence, the main loop shows global progress (= number of steps launched), each step show it's progress too in a separate progress window, and it must be possible to stop a step (and continue with the rest) or stop globally.

            jsulmJ 1 Reply Last reply
            0
            • J JanLaloux

              The sleep is just for the demo program of course, it replaces the processing that happens in the real program. I could write a for loop with some dummy calculations, but that does not change fundamentally the setup: a main loop that launches one step after the other in sequence, the main loop shows global progress (= number of steps launched), each step show it's progress too in a separate progress window, and it must be possible to stop a step (and continue with the rest) or stop globally.

              jsulmJ Online
              jsulmJ Online
              jsulm
              Lifetime Qt Champion
              wrote last edited by
              #7

              @JanLaloux said in Problems with stacked Progress Dialogs:

              it replaces the processing that happens in the real program

              This does not change anything.
              As long as you're doing long lasting processing in UI thread you're blocking the user interface.
              Move this processing to other threads.

              https://forum.qt.io/topic/113070/qt-code-of-conduct

              J 1 Reply Last reply
              1
              • Christian EhrlicherC Christian Ehrlicher

                What has winmain to do with QtCreator? Simply use main()

                J Offline
                J Offline
                JanLaloux
                wrote last edited by
                #8

                @Christian-Ehrlicher: This is VisualStudio, depending on the type of project you choose you will get another main statement. If you change it into a simple "main()" you will get a linking error that "WinMain" is missing.

                Pl45m4P 1 Reply Last reply
                0
                • jsulmJ jsulm

                  @JanLaloux said in Problems with stacked Progress Dialogs:

                  it replaces the processing that happens in the real program

                  This does not change anything.
                  As long as you're doing long lasting processing in UI thread you're blocking the user interface.
                  Move this processing to other threads.

                  J Offline
                  J Offline
                  JanLaloux
                  wrote last edited by
                  #9

                  @jsulm I don't agree. If there is only the global progress window (no step progress shown), the progress dialog remains responsive and it can be cancelled at any time, no other thread is needed. You have of course to check regularly if the cancel button was pressed, and the QProgressDialog::canceled signal is connected to a slot for this.
                  Also, a step progress dialog is reactive as it should, no running in another thread needed. It is only when two progress dialogs are created and running at the same time that the problems arise

                  1 Reply Last reply
                  0
                  • J JanLaloux

                    @Christian-Ehrlicher: This is VisualStudio, depending on the type of project you choose you will get another main statement. If you change it into a simple "main()" you will get a linking error that "WinMain" is missing.

                    Pl45m4P Offline
                    Pl45m4P Offline
                    Pl45m4
                    wrote last edited by
                    #10

                    @JanLaloux said in Problems with stacked Progress Dialogs:

                    This is VisualStudio, depending on the type of project you choose you will get another main statement. If you change it into a simple "main()" you will get a linking error that "WinMain" is missing.

                    I don't agree ;-)
                    You don't have to use winMain manually for Qt apps. Not in Qt Creator, not in Visual Studio.

                    Sure, if you pick Console Application, you can't get rid of that.


                    If debugging is the process of removing software bugs, then programming must be the process of putting them in.

                    ~E. W. Dijkstra

                    1 Reply Last reply
                    0
                    • Pl45m4P Pl45m4

                      @JanLaloux said in Problems with stacked Progress Dialogs:

                      Is something wrong in my setup?

                      Is the code above just an example or your real code?
                      Why you use winMain? Qt already does that for you when you are on Windows and create a new Qt Widget App.

                      S Offline
                      S Offline
                      SimonSchroeder
                      wrote last edited by
                      #11

                      @Pl45m4 said in Problems with stacked Progress Dialogs:

                      Is the code above just an example or your real code?

                      I guess this question is asked because your source makes most of us squeam in horror. Others have already pointed out that you should use regular main() for Qt programs. It doesn't help that you are creating your own argc and argv variables to be passed to the QApplication constructor.

                      It is really bad practice to use global variables. Everything above your main function should go into a MainWindow class derived from QMainWindow.

                      You should also avoid using pointers whereever possible. In your particular case there is no reason for Application and mainWindow to be pointers (but mainWindow should also be a local variable). Theoretically, you are leaking memory. (Practically, it doesn't matter because the OS will clean up after you.)

                      @JanLaloux said in Problems with stacked Progress Dialogs:

                      I don't agree. If there is only the global progress window (no step progress shown), the progress dialog remains responsive and it can be cancelled at any time, no other thread is needed. You have of course to check regularly if the cancel button was pressed, and the QProgressDialog::canceled signal is connected to a slot for this.

                      My experience is totally different. I have never seen a smooth animation of the progress bar when doing it this way. And you also have to hit the cancel button at the right time to be able to cancel progress. This is the reason why you'll find many discussions in this forum using QApplication::processEvents to make the progress dialog somehow work properly. But, everybody will tell you not to use QApplication::processEvents. Your actual computation will take way longer if you do this. The only proper way to do this is actually to use a separate thread. I have simplified this for myself and put it into a library: https://github.com/SimonSchroeder/QtThreadHelper. Just use workerThread([](){ /* do stuff */ }); to run any function in another thread (this is derived from QThread::create followed by start()). If you want to wait for the work to finish, use workerThreadJoin() instead. There's another helper guiThread() which I typically use to update the progress bar. You'll find an example in the readme of my lib.

                      J 1 Reply Last reply
                      2
                      • S SimonSchroeder

                        @Pl45m4 said in Problems with stacked Progress Dialogs:

                        Is the code above just an example or your real code?

                        I guess this question is asked because your source makes most of us squeam in horror. Others have already pointed out that you should use regular main() for Qt programs. It doesn't help that you are creating your own argc and argv variables to be passed to the QApplication constructor.

                        It is really bad practice to use global variables. Everything above your main function should go into a MainWindow class derived from QMainWindow.

                        You should also avoid using pointers whereever possible. In your particular case there is no reason for Application and mainWindow to be pointers (but mainWindow should also be a local variable). Theoretically, you are leaking memory. (Practically, it doesn't matter because the OS will clean up after you.)

                        @JanLaloux said in Problems with stacked Progress Dialogs:

                        I don't agree. If there is only the global progress window (no step progress shown), the progress dialog remains responsive and it can be cancelled at any time, no other thread is needed. You have of course to check regularly if the cancel button was pressed, and the QProgressDialog::canceled signal is connected to a slot for this.

                        My experience is totally different. I have never seen a smooth animation of the progress bar when doing it this way. And you also have to hit the cancel button at the right time to be able to cancel progress. This is the reason why you'll find many discussions in this forum using QApplication::processEvents to make the progress dialog somehow work properly. But, everybody will tell you not to use QApplication::processEvents. Your actual computation will take way longer if you do this. The only proper way to do this is actually to use a separate thread. I have simplified this for myself and put it into a library: https://github.com/SimonSchroeder/QtThreadHelper. Just use workerThread([](){ /* do stuff */ }); to run any function in another thread (this is derived from QThread::create followed by start()). If you want to wait for the work to finish, use workerThreadJoin() instead. There's another helper guiThread() which I typically use to update the progress bar. You'll find an example in the readme of my lib.

                        J Offline
                        J Offline
                        JanLaloux
                        wrote last edited by
                        #12

                        @SimonSchroeder Waw, this is realy helpfull (and not just a squeam in horror ;-), thank ypu! I will have a close look into your library and rework the whole lot in this way. Will take some time, but I'll give you feedback when it's done.

                        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