Problem
I want to use a QTimer
to update a derived QSplashScreen
that draws a progress bar (using paint commands, not a widget) to estimate when the program will start running.
By necessity, this happens prior to the exec
call of the QCoreApplication
. I've gotten this to work (in release mode only) on both X11 and Windows, by putting a timer in a second thread, and calling a function in the splash screen which updates the progress and repaints the widget. However, this doesn't work in debug mode as it produces the following error:
"ASSERT failure in QCoreApplication::sendEvent: "Cannot send events to objects owned by a different thread."
I'm not really worried about this assertion as the code doesn't crash in release and it's just a splash screen, however I need to be able to run the program in debug so I'd like to either a) refactor the code so it doesn't trigger the assertion, or b) dissable this particular assertion.
Tried:
- Using
update()
instead ofrepaint()
. This doesn't cause an assertion, but it also doesn't repaint because the main thread is too busy loading in the shared libraries, etc, and the timer events don't get processed until I'm ready to callfinish
on the splash screen. - starting
QTimer
in main loop. Same result as above. - Using a
QT::QueuedConnection
. Same result.
Main
#include <QApplication>
#include <QtGui>
#include <QTimer>
#include <QThread>
#include "mySplashScreen.h"
#include "myMainWindow.h" // contains a configure function which takes a
// LONG time to load.
int main( int argc, char* argv[] )
{
// estimate the load time
int previousLoadTime_ms = 10000;
QApplication a(argc, argv);
MySplashScreen* splash = new MySplashScreen(QPixmap(":/splashScreen"));
// progress timer. Has to be in a different thread since the
// qApplication isn't started.
QThread* timerThread = new QThread;
QTimer* timer = new QTimer(0); // _not_ this!
timer->setInterval(previousLoadTime_ms / 100.0);
timer->moveToThread(timerThread);
QObject::connect(timer, &QTimer::timeout, [&]
{
qApp->processEvents(); splash->incrementProgress(1);
});
QObject::connect(timerThread, SIGNAL(started()), timer, SLOT(start()));
timerThread->start();
splash->show();
a.processEvents();
myMainWindow w;
QTimer::singleShot(0, [&]
{
// This will be called as soon as the exec() loop starts.
w.configure(); // this is a really slow initialization function
w.show();
splash->finish(&w);
timerThread->quit();
});
return a.exec();
}
Splash Screen
#include <QSplashScreen>
class MySplashScreen : public QSplashScreen
{
Q_OBJECT
public:
MySplashScreen(const QPixmap& pixmap = QPixmap(), Qt::WindowFlags f = 0)
: QSplashScreen(pixmap, f)
{
m_pixmap = pixmap;
}
virtual void drawContents(QPainter *painter) override
{
QSplashScreen::drawContents(painter);
// draw progress bar
}
public slots:
virtual void incrementProgress(int percentage)
{
m_progress += percentage;
repaint();
}
protected:
int m_progress = 0;
private:
QPixmap m_pixmap;
};
MyMainWindow
#include <QMainWindow>
class myMainWindow : public QMainWindow
{
public:
void configure()
{
// Create and configure a bunch of widgets.
// This takes a long time.
}
}