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 the MainWindow), most probably because the show() 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!


  • Qt Champions 2016

    Hi
    I had same issue so i used

    void 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);


  • Qt Champions 2016

    @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!


  • Lifetime Qt Champion

    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.


  • Qt Champions 2016

    @l3u_
    Hi
    Mine is like this

    ctor..
    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?


  • Moderators

    @l3u_ In my opinion the timer solution is in this case not very clean. As you already noticed you do not know how long it will take. The solution from @mrjj is is much cleaner.


  • Qt Champions 2016

    @l3u_
    Well you can time it and find a good value.
    or make it large enough that it will always be enough.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.