PDA

View Full Version : Problems launching a QProgressDialog after main window is made visible



jcbaraza
14th November 2019, 00:02
Hi all,

I'm developing a desktop (Windows 10) application that needs to perform some very slow initilization (including file search in all local units). For this reason, I want to issue a QProgressDialog just after the main window is visible to inform the user about the progress of the initialization.

I've been loooking in forums for several days, and this problem seemed to be solved in up to four ways, but I can't it get it with any of the four methods.

So far, I'm getting my best results by overloading showEvent, and launching my initialization function (just once by using a control variable) inside showEvent after the main window is visible (see option 1 in code below). However, the result is unsatisfactory because, although I can see both the main window and the QProgressDialog, this does not show any motion in the progress bar until a (fake) QMessageBox is issued after the initialization process (please see the pictures below). If the message were not issued, the QProgressDialog would never appear.

13286

13287

By the way, is it possible to change the aspect of the bar in the QProgressDialog to make its text invisible, and so the bar will expand to the width of the dialog (just like in Qt Design editor, where setting "textVisible" = false gets this effect)? Or this can be made only by customizing the bar with "setBar"?

With the other three options proposed in forums I get absolutely worse results, including a Debug error. So, I must be doing something wrong, but I can't guess what. Can anyone teel me what I'm doing wrong, please?

I attach all the code (MainWindow.h and MainWindow.cpp; I don't think MainWindow.ui is necessary) in case someone may want to test. Note that all four options are implemented (although the three "worse" ones are commented) and can be tested.

MainWindow.h


#include <QDir>
#include <QFile>
#include <QIODevice>
#include <QTextStream>
#include <QProcess>
#include <QStorageInfo>
#include <QTimer>
#include <windows.h>

#include <QWidget>
#include <QMessageBox>

namespace Ui
{
class MainWindow;
}

class MainWindow : public QMainWindow
{
Q_OBJECT

protected:
void showEvent(QShowEvent *ev);

// Options 1, 2 and 3: Comment signals declaration.
// Option 4: Uncomment signal declaration.
//signals:
// void window_loaded();

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

private slots:
void on_actionInitializeTestInit();
void on_actionExit_triggered();

private:
QLabel* Label1;
QLabel* Label2;
bool TestInit_started;

void CreateStatusBar();
void SearchFiles(const QString &start_path, QStringList &File_list);

Ui::MainWindow *ui;

};

#endif // MAINWINDOW_H

MainWindow.cpp


#include <QtGlobal>
#include "MainWindow.h"
#include "ui_MainWindow.h"

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);

TestInit_started = false;

// Options 1, 2 and 3: Comment "connect"
// Option 4: Uncomment "connect" to connect my initialization function to
// a signal which will be issued after showing MainWindow
// connect(this, SIGNAL(MainWindow::window_loaded()), this, SLOT(on_actionInitializeTestInit()), Qt::ConnectionType(Qt::QueuedConnection | Qt::UniqueConnection));

QApplication::setOverrideCursor(QCursor(Qt::WaitCu rsor));

// Initialize some internal elements

QApplication::restoreOverrideCursor();
}

MainWindow::~MainWindow()
{
delete ui;
}

void MainWindow::showEvent(QShowEvent *ev)
{
QMainWindow::showEvent(ev);
if (!TestInit_started)
// Option 1: Comment "QMetaObject", "on_action..." and "emit", and
// uncomment "QTimer"
QTimer::singleShot(0, this, SLOT(on_actionInitializeTestInit()));
// Option 2: Comment "QTimer", "on_action..." and "emit", and
// uncomment "QMetaObject"
// RESULT: Debugging error: Dead lock detected in BlockingQueuedConnection: Receiver is MainWindow(0x78fd00)
// QMetaObject::invokeMethod(this, "on_actionInitializeTestInit", Qt::ConnectionType(Qt::QueuedConnection | Qt::UniqueConnection));
// Option 3: Comment "QTimer", "QMetaObject" and "emit", and uncomment
// "on_action..."
// on_actionInitializeTestInit();
// Option 4: Comment "QTimer", "QMetaObject" and "on_action...", and
// uncomment "emit" to issue signal "window_loaded"
// emit MainWindow::window_loaded();
}

void MainWindow::on_actionInitializeTestInit()
{
if (TestInit_started) // Yes, this check is written twice. Just in case...
return;

TestInit_started = true;

QApplication::setOverrideCursor(QCursor(Qt::WaitCu rsor));

// Launch progress dialog. It's configured as a busy indicator because the
// task to perform next is very slow. By the way, is it possible to change
// the aspect of the bar to make its text invisible (just like in the
// Design editor, where setting "textVisible" = false gets), and so the bar
// will expand to the width of the dialog? Or that can be made only by
// customizing the bar with "setBar"?
// Regarding the progress dialog refresh, I've tried with "show", "repaint"
// "update" and "processEvents". If I don't "show", the progress dialog
// doesn't appear; if I don't "processEvents", the progres bar in the
// progress dialog doesn't appear. So, this is my best combination so far.
QProgressDialog progress(this);
// progress.setWindowModality(Qt::WindowModal);
progress.setMinimumWidth(350);
progress.setWindowTitle("Initializing TestInit");
progress.setLabelText("Searching files. This operation may last some minutes. Please wait...");
progress.setCancelButton(nullptr);
progress.setRange(0, 0);
progress.setAutoClose(true);
// progress.setValue(1);
progress.show();
// progress.repaint();
// progress.update();
qApp->processEvents();



// First initialization step: Search in the whole system important files
// for the application. This is a very slow operation, and that's why
// the progress dialog is used as a busy indicator.
// You can customize the file name to search in SearchFiles function below.
QStringList File_list;
foreach (const QStorageInfo &storage, QStorageInfo::mountedVolumes())
{
if (storage.isValid() && storage.isReady() && !storage.isReadOnly() &&
((storage.device().startsWith("\\\\?")) || (storage.device().startsWith("/dev/sda0"))))
{
QString rootpath(QDir::toNativeSeparators(storage.rootPath ()));
SearchFiles(rootpath, File_list);
}
}


// This block is included just for entertainment. After the search, it
// issues an info message showing the files found.
// In the real application this message will not appear. The problem is
// that the progress dialog does not appear until this message is issued.
// Why?
QApplication::restoreOverrideCursor();
QString allfiles;
for (int i = 0; (i < File_list.size()); i++)
{
if (allfiles.size() > 0)
allfiles += '\n';
allfiles += File_list.at(i);
}
QMessageBox::information(this, "Files found", allfiles, QMessageBox::Ok, QMessageBox::Ok);
QApplication::setOverrideCursor(QCursor(Qt::WaitCu rsor));


// Other initializations are performed here. These are faster than the
// previous one, and the progress dialog now behaves as usual.
progress.setLabelText("Doing something else (1).");
progress.setRange(0, 1);
progress.setAutoClose(true);
progress.setValue(1);
qApp->processEvents();
// Do something

progress.setLabelText("Doing something else (2).");
progress.setValue(1);
qApp->processEvents();
// Do something

// And so on...

progress.setValue(5);
qApp->processEvents();
progress.close();

QApplication::restoreOverrideCursor();
}
void MainWindow::on_actionExit_triggered()
{
close();
}

void MainWindow::SearchFiles(const QString &start_path, QStringList &File_list)
{
QString targetfile("config.xml"); // Put here the filename you may want to test
QString iparameters_fich = QDir::toNativeSeparators(start_path + '/' + targetfile);
if (QFile(iparameters_fich).exists())
File_list << iparameters_fich;
else
{

// Search configuration files in subdirectories
QStringList nameFilter;
QFileInfoList lstDirectoriesInfo =
QDir(start_path).entryInfoList(nameFilter,
QDir::NoDotAndDotDot | QDir::Dirs,
QDir::Name);
for (int i = 0; (i < lstDirectoriesInfo.size()); i++)
SearchFiles(lstDirectoriesInfo.at(i).absoluteFileP ath(), File_list);
}
}


Regarding the QProgressDialog, I've tried to implement modal and non-modal, and I've also tried with different values: 0, 1 and no value. The result is the same in all cases.

Thanks a lot in advance. Regards,

jcbaraza

Ginsengelf
14th November 2019, 15:10
Hi, it might help to add qApp->processEvents(); to your SearchFiles() method. The reason is that both your QProgressDialog and your long search operation take place in GUI thread, and the program does not return to the Qt event loop while your search is running.

Ginsengelf

jcbaraza
14th November 2019, 23:56
Hi, it might help to add qApp->processEvents(); to your SearchFiles() method. The reason is that both your QProgressDialog and your long search operation take place in GUI thread, and the program does not return to the Qt event loop while your search is running.

Ginsengelf

Wow! That worked! The bar freezes for a while now and then, but now it really looks like working. Thank you so much Gingsengelf!

And how about my other question regarding the size of the progress bar? Is it possible to make it expand by setting any property, in a similar way as setting "textVisible" = false in Qt Design editor?

jcbaraza
16th November 2019, 02:19
Wow! That worked! The bar freezes for a while now and then, but now it really looks like working. Thank you so much Gingsengelf!

And how about my other question regarding the size of the progress bar? Is it possible to make it expand by setting any property, in a similar way as setting "textVisible" = false in Qt Design editor?

I've discovered on my own. The answer is no, but doing is relatively easy. It's something like this:


QProgressDialog progress(this);
QProgressBar mybar;
mybar.setTextVisible(false);
progress.setBar(&mybar);

And, in case you may need to change the progress display mode from busy indicator to progress bar, you only have to set the text of the progress bar visible again, and change the range:


mybar.setTextVisible(true);
progress.setRange(0, n);
progress.setValue(i); // "i" means any value representing the percentage of the task performed so far
// Continue doing tasks and varying Value
// ...
progress.setValue(n);
progress.close();


Regards,