Reputation: 7915
I have a simple splash screen extension which should show a little animation (animated gif) while the user waits for the main window. The problem is, that only the first frame is shown instead of all:
class SplashScreen : public QSplashScreen
{
Q_OBJECT
public:
explicit SplashScreen(const QPixmap& pixmap, const QString& animation, Qt::WindowFlags flags = 0);
protected:
void paintEvent(QPaintEvent* event);
signals:
void frameChanged();
private slots:
void convertFrameChanged(int)
{
repaint();
}
private:
QMovie movie;
};
SplashScreen::SplashScreen(const QPixmap& pixmap, const QString& animation, Qt::WindowFlags flags)
: QSplashScreen(pixmap, flags),
movie(animation)
{
movie.start();
connect(&(movie), SIGNAL(frameChanged(int)), this, SLOT(convertFrameChanged(int)));
}
void SplashScreen::paintEvent(QPaintEvent* event)
{
QSplashScreen::paintEvent(event);
QPixmap frame = movie.currentPixmap();
QRect rect = frame.rect();
rect.moveCenter(this->rect().center());
if (rect.intersects(event->rect()))
{
QPainter painter(this);
painter.drawPixmap(rect.left(), rect.top(), frame);
}
}
EDIT:
Tried to invoke repaint with QTimer in SplashScreen constructor:
QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(doRefresh()));
timer->start(1000);
Added slot to the Splashscreen:
void doRefresh()
{
repaint();
}
But this didn't work, too. doRefresh isn't invoked. It seems the QTimer also requires an alread existing event loop.
Upvotes: 1
Views: 4325
Reputation: 1
QTimer::singleShot(1500, &splash, [&splash] () {
QPixmap pxm("img1.png");
if (pxm.isNull()==false) {
splash.setPixmap(pxm);
}});
QTimer::singleShot(3000, &splash, [&splash] () {
QPixmap pxm("img2.png");
if (pxm.isNull()==false) {
splash.setPixmap(pxm);
}});
Upvotes: 0
Reputation: 37697
Problem is quite profound. To have animation working you have to allow process events in main thread. On other hand initialization process is also performed on main thread.
First approach is add those app.processEvents()
as often as possible but this will limit frame rate so badly that it is useless.
To fix it properly you have answer two important question:
Once I've had application where during initialization large index was build. This took about 40 seconds so quite a lot. I've moved building index to separate thread using QtConcurrent::run(this, &MyClass::MyMethodToBuildIndex)
and showing some progress by emitting signals periodically from MyClass::MyMethodToBuildIndex
(default automatic connection did the whole trick).
If you have similar log time initialization this solution will enable animation of your QMovie
out of the box, problem will be just smart connection of signal and slots so windows will be shown and hidden with proper timings.
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
SplashScreen splash();
splash.show();
app.processEvents(); // need to process events manually
QMainWindow window;
SomeLogicClass data;
window.setData(&data);
connect(&data, SIGNAL(initializationProgressChanged(int)),
&spash, SLOT(setProgress(int)));
// show main window when initialization is finished
connect(&data, SIGNAL(initializationFinished()),
&window, SLOT(show()));
// close splash when initialization is finished
connect(&data, SIGNAL(initializationFinished()),
&spash, SLOT(close()));
// this line I usually hide behind some method like: startBackgroundInitialization
QtCuncurent::run(&data, &SomeLogicClass::heavyInitialization);
return app.exec();
}
If you need to use QSplashScreen::finish
instead close
then QSignalMapper
can help.
Note that last argument of connect is default value Qt::AutoConnection
which will enforce slots to be run in main thread.
Upvotes: 1
Reputation: 27611
Assuming that the splash screen is displayed before the QApplication::exec() function is called, then it's likely that events aren't being processed, so you need to call processEvents on the QApplication object.
Note the help example: -
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QPixmap pixmap(":/splash.png");
QSplashScreen splash(pixmap);
splash.show();
app.processEvents(); // need to process events manually
...
QMainWindow window;
window.show();
splash.finish(&window);
return app.exec();
}
In this situation it discusses calling raise() on the splash screen, with a QTimer to ensure it stays on top, but needs the processEvents call for the QTimer to function.
As it states in Qt help for dismissing the splash screen when the mouse button is clicked: -
Since the splash screen is typically displayed before the event loop has started running, it is necessary to periodically call QApplication::processEvents() to receive the mouse clicks.
The same will likely apply to the animated gif.
Upvotes: 1