Unsolved QMessageBox is not positioned correctly when shown during startup
-
Hi :-)
I have a program that takes one command line parameter (the file to load). If something goes wrong while loading the file, a
QMessageBox
is shown. It's not placed correctly (not centered on theMainWindow
), most probably because theshow()
process is not finished yet when the load function is called.Here's a minimalistic example for producing the problem (at least on Linux/KDE):
main.cpp:
#include "MainWindow.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication app(argc, argv); MainWindow mainWindow; mainWindow.show(); mainWindow.loadTournament(); return app.exec(); }
MainWindow.h:
#include <QMainWindow> class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(); void loadTournament(); };
MainWindow.cpp:
#include "MainWindow.h" #include <QMessageBox> MainWindow::MainWindow() { resize(900, 700); } void MainWindow::loadTournament() { QMessageBox::critical(this, tr("foo"), tr("bar"), QMessageBox::Ok); }
I tried to add
QApplication::processEvents();
before the load function call, but it doesn't change anything.I also tried to add
QStringList arguments = QCoreApplication::arguments(); if (arguments.count() > 1) { loadTournament(arguments[1]); }
to the end of the main window's constructor, but the behavior is exactly the same, also with a
QApplication::processEvents();
before.I then tried to use a
QTimer
with the following code added to the end of the constructor:QTimer::singleShot(0, this, &MainWindow::checkLoadTournament);
and
void MainWindow::checkLoadTournament() { QStringList arguments = QCoreApplication::arguments(); if (arguments.count() > 1) { loadTournament(arguments[1]); } }
added. This also produces the very same wrong placement of the
QMessageBox
.If I increase the
QTimer
wait value (to e. g. 100 msecs), it works. But this seems a bit hacky to me, because I can't be sure if the main window is actually shown within this period of time.Is there a way to either know if the main window showing and placement is done and finished correctly and wait for this to be done before showing the
QMessageBox
or to place it to the right position before?Thanks in advance for all help!
-
Hi
I had same issue so i usedvoid MainWindow::showEvent ( QShowEvent* ev ) { QMainWindow::showEvent ( ev ); emit MainWindowLoaded(); }
The other classes then use the MainWindowLoaded signal to know mainwin should be up now.
Q_ASSERT( connect ( this, SIGNAL ( MainWindowLoaded() ), this, SLOT ( WindowLoaded() ),
Qt::ConnectionType ( Qt::QueuedConnection | Qt::UniqueConnection ) ));This is not perfect as the event might be sent multiple times etc but worked fine for my use case.
full sample here
https://stackoverflow.com/questions/14356121/how-to-call-function-after-window-is-shown -
Hi @l3u_
The zero length timer is the usual way to go. It should start when the system thinks it has nothing else to do. But paint events are always a bit tricky. If there are more then one in the queue, they get delayed and bundled into one call for efficiency. This "feature" may cause your problem.
If it appeals to you you can try another hack: Call the timer
QTimer::singleShot(0, this, &MainWindow::checkLoadTournament);
at the end of your paintEvent, proteced by a flag that is will only be called once.EDIT: Oops, almost the same as above.
-Michael.
-
@mrjj I also tried to put the code inside the MainWindow's
showEvent
at the end, but even this did not make the message box appear at the right position … -
@l3u_ said in QMessageBox is not positioned correctly when shown during startup:
void MainWindow::loadTournament()
{
QMessageBox::critical(this, tr("foo"), tr("bar"), QMessageBox::Ok);
}try replace this = 0;
QMessageBox::critical(0, tr("foo"), tr("bar"), QMessageBox::Ok); -
@l3u_
hi
but you do it directly in event func.
Its not finished happening yet
I do via eventloop and just tested
with QMessageBox::critical does show centered. -
@Taz742 Well, setting
0
as parent causes (as I would expect it …) the message box to be displayed centered on the screen … -
@mrjj Okay, when doing it via a signal, I could know when the window is set up completely. But how could I do the actual function call? Using a timer that checks periodically if some variable is set to true by the slot called after the show event? I mean, I don't want to call the load function always when the window is shown, but only if it's requested on the command line!
-
Hi,
Just a quick note: never put any function all in a Q_ASSERT or a classic assert expression. The function will never be called in release mode. Store the result of the function and assert on that.
-
@l3u_
Hi
Mine is like thisctor.. Q_ASSERT( connect ( this, SIGNAL ( MainWindowLoaded() ), this, SLOT ( WindowLoaded() ), Qt::ConnectionType ( Qt::QueuedConnection | Qt::UniqueConnection ) )); void MainWindow::showEvent ( QShowEvent* ev ) { QMainWindow::showEvent ( ev ); emit MainWindowLoaded(); } // the slot do thestuff void MainWindow::WindowLoaded() { real stuff
However, as m.sue says, one shot timer is might just be cleaner for you :)
-
@mrjj said in QMessageBox is not positioned correctly when shown during startup:
However, as m.sue says, one shot timer is might just be cleaner for you :)
I also think so, but what is the reason that the One-Shot-Timer with 0 delay does not change the result whereas the one with 100 ms delay causes the message box to be positionned correctly? Or asking the other way: what delay should I choose to be sure that the main window is always setup completely?
-
-
@l3u_
Well you can time it and find a good value.
or make it large enough that it will always be enough.