Search code examples
c++qtqthreadqtconcurrentqsplashscreen

Qt Gui not updated because of threading issue


I am using a third party library which takes 60-90 seconds to dynamically load several libraries. It is an unfortunate design choice, but I can't change who the built their code.

I am trying to use a QSplashScreen to at least tell the user to wait while I'm doing this one-time loading in the background. The problem is that the splash screen is not painting. I can see a window of non-painted space while the library loads. I can see the splash screen afterwards before I close it.

I looked at similar questions (e.g. Qt Splash Screen not showing) but nothing seems to resolve my problem. I have tried loading an empty QPixmap and just giving it a solid color. That doesn't show up either.

QApplication a(argc, argv);
QPixmap pxl("icon.bmp");
QSplashScreen qss(pxl);
qss.show();
qss.showMessage(QString::fromStdString("Please wait... Loading"));
a.processEvents();

MainWindow w;
//thread is blocked
w.preLoad();//this is where the lengthy process takes place
w.show();

qss.finish(&w);

I'd like to ensure that it at least paints once before I start my loading process.

------------------------EDIT-------------------------------

Let me reiterated that the call to preLoad is BLOCKING the thread. This is not an option. I have tried a separate thread for that process. I have tried with a separate thread for the splash screen as well (turning it on and then finishing whne the other thread is done). I have tried using semaphore between the two threads to accomplish this and while everything works (including the splash screen) it takes 200-800 seconds to load. That is simply not acceptable. Thus I would like to see if there is away around it from this perspective.

-------------------------Final solution--------------------------------

Thanks to the comments below I was made aware that Qt has its own threading functionality. All the problems I was seeing appear to be caused by the interplay of std::thread and Qt's own implementation.

I have a partial solution which does work. It's not as neat as it could be, but I wanted to include it in the question thread.

//in the main method code described above
MainWindow w;
w.preLoad();
while(w.IsLoading())
{
  //waiting on semaphore signaling loading is complete
  //this part could probably be done better with QThread 
  //and signals, but it is a quick fix for now
  std::this_thread::sleepfor(std::chrono::milliseconds(500));
  a.processEvents();
}
w.show();

qss.finish(&w);


//In the MainWindow class
void MainWindow::preLoad()
{
  loading=true;//semaphore to stall main thread
  QFuture<void> future = QtConcurrent::run(this, &MainWindow::LongMethod);
}

void MainWindow::LongMethod()
{
  thirdPartyLibrary.impossibleCall();
  loading=false;//this handles my semaphore
}

Solution

  • Every GUI is executed in an infinite loop, so Qt also uses it, but the blocking tasks generate that the loop is not executed correctly, showing inadequate behaviors like the one you observe.

    If one wants to execute blocking tasks it is advisable to execute it in another thread for it Qt provides several possibilities:

    I recommend the following link for you to choose the right option for your case.

    If you want to update the GUI view with the information generated in another thread it is advisable to use signals and slots, or use QtConcurrent.

    GUI Thread and Worker Thread

    As mentioned, each program has one thread when it is started. This thread is called the "main thread" (also known as the "GUI thread" in Qt applications). The Qt GUI must run in this thread. All widgets and several related classes, for example QPixmap, don't work in secondary threads. A secondary thread is commonly referred to as a "worker thread" because it is used to offload processing work from the main thread.

    Another way is to force the GUI to update for this we can use qApp->processEvents().

    References: