Search code examples
c++qtsplash-screenqsplashscreen

Is there any way to make really sure QSplashScreen has been repainted on the screen?


I have a problem that on Linux with Xorg (Ubuntu 14.04) and Qt 5.5.1 QSplashScreen isn't painted until I get to the event loop. Even if I call QApplication::processEvents() multiple times, it still isn't painted, even after 1000 calls, although the window is already on the screen, retaining the original pixels which were there before the app launched, thus being effectively invisible*. From this answer I got an idea of using a timed loop of calling QApplication::processEvents(), like here:

#include <QThread>
#include <QApplication>
#include <QSplashScreen>

int main(int argc, char** argv)
{
    QApplication a(argc,argv);

    QSplashScreen splash;
    splash.show();
    splash.showMessage("Loading...");
    // The hack to try to ensure that splash screen is repainted
    for(int i=0;i<30;++i)
    {
        QThread::usleep(1e3);
        a.processEvents();
    }

    QThread::usleep(5e6); // simulate slow loading process
    splash.showMessage("Finished");

    return a.exec();
}

The above code actively sleeps for 30 ms in an attempt to make QSplashScreen repaint. This works for me, but I'm not sure that it'll always work e.g. on a busy/slow CPU or in whatever other conditions (the magic value of 30 iterations was found empirically).

Another, quite intrusive in general, way would be to do all the necessary loading in another thread, only to make sure that QSplashScreen in the main thread does have an active message queue. Due to the need to considerably redo the main program, this looks not too good of a solution.

So, is there any way to make sure that QSplashScreen has been repainted, so that its window doesn't contain garbage, and only then to proceed with long blocking loading process?


* I discovered this when I moved a window behind the splash screen


Solution

  • One way to avoid the unknowable a priori magic timeout is to wait for an exact event: the paint event. On X11 it appears to come with a delay. To do this waiting we'll have to subclass QSplashScreen and override QSplashScreen::paintEvent(), like here:

    #include <QThread>
    #include <QApplication>
    #include <QSplashScreen>
    
    class MySplashScreen : public QSplashScreen
    {
        bool painted=false;
    
        void paintEvent(QPaintEvent* e) override
        {
            QSplashScreen::paintEvent(e);
            painted=true;
        }
    public:
        void ensureFirstPaint() const
        {
            while(!painted)
            {
                QThread::usleep(1e3);
                qApp->processEvents();
            }
        }
    };
    
    int main(int argc, char** argv)
    {
        QApplication a(argc,argv);
    
        MySplashScreen splash;
        splash.show();
        splash.showMessage("Loading...");
        splash.ensureFirstPaint();
    
        QThread::usleep(5e6); // simulate slow loading process
        splash.showMessage("Finished");
    
        return a.exec();
    }