About QMovie : play gif animation only once



  • I am having a QLabel that I want to display a gif animation before an image. I have trouble making the gif play only once :

    QMovie* movie = new QMovie(":/images/rolling.gif");
    m_ui->label->setMovie(movie);
    movie->start();
    connect(movie, &QMovie::finished, movie, &QMovie::stop);
    
    connect(movie, &QMovie::finished,
                this, [&](){
            QPixmap image(":/images/dice.png");
            m_ui->label->setPixmap(image);
            m_ui->label->show();
        });
    //The animation goes on forever
    

    I can also do this (kind of successfully*) with a timer :

    QTimer* timer = new QTimer(this);
    QMovie* movie = new QMovie(":/images/rolling.gif");
    m_ui->label->setMovie(movie);
    movie->start();
    connect(timer, &QTimer::timeout, movie, &QMovie::stop);
    connect(timer, &QTimer::timeout, this ,&QtDice::stopped_movie);
    timer->start(1000);
    connect(this, &QtDice::stopped_movie,
            this, [&](){
         QPixmap image(":/images/dice.png");
         m_ui->label->setPixmap(image);
         m_ui->label->show();
        });
    

    *Indeed the QLabel is updated with the image after the animation ends.

    Besides the fact that I want a cleaner (not timer based) solution, when I load other images to QLabel, they are shown for a brief moment and quickly images/dice.png is shown again. Apparently that's because of the whole signal thing that is connected there.

    Have I to disconnect QMovie's signals?

    EDIT : I forgot to mention that this code belongs to QtDice constructor.


  • Qt Champions 2017

    Hi
    The actual GIF animation format controls this.
    It has a key for loop count.
    So you can just edit it and remove the looping.



  • The actual GIF animation format controls this.

    So there isn't something the API could do dynamically inside the application.

    Thank you very much .


  • Moderators

    @Petross404_Petros-S said in About QMovie : play gif animation only once:

    So there isn't something the API could do dynamically inside the application.

    Not directly.

    But as a workaround, you could try to listen for the frameChanged() signal from QMovie, and stop it yourself when you reach the final frame.



  • @JKSH said in About QMovie : play gif animation only once:

    @Petross404_Petros-S said in About QMovie : play gif animation only once:

    So there isn't something the API could do dynamically inside the application.

    Not directly.

    But as a workaround, you could try to listen for the frameChanged() signal from QMovie, and stop it yourself when you reach the final frame.

    I guess I have to count the frames in order to stop it at the very last frame. I hope int QMovie::frameCount() const can do the job.

    I will post again when I try this.



  • Ok frameChanged signal helped a lot :

    ....
    movie->start();
    ....
    connect(movie, &QMovie::frameChanged, this,
    	[movie]()
    	{
             //For some reason == movie->frameCount() crashes, so... *
    		if(movie->currentFrameNumber() == (movie->frameCount() - 1))
    		{
    			movie->stop();
    			//Explicity emit finished signal so that label **
    			//can show the image instead of a frozen gif
    			//Also, double check that movie stopped before emiting
    			if (movie->state() == QMovie::NotRunning)
    			{
    				emit movie->finished();
    			}
    		}
    	}
    );
    
    //Once finished is emitted, update the label and
    //the status of m_button and action_Cast_the_dice
    //Also emit dice_stopped_rolling
    connect(movie, &QMovie::finished,
    	this, [&]()
    	{
    		m_ui->label->setPixmap(image);
    		m_ui->label->show();
    		m_ui->m_button->setEnabled(true);
    		m_ui->m_button->setFocus();
    		m_ui->action_Cast_the_dice->setEnabled(true);
    		emit dice_stopped_rolling();
    	}
    );
    //Too big lamdas, I have to write some slots for these...
    

    *frameCount returns 20 for the specific gif file, but for some reason I must stop at frame #19. That's an easy workaround to remember.

    ** What boggled my mind is that QMovie::stop() doesn't emit finished as I expected from the description in Detailed Description.

    ...When the movie is done, QMovie emits finished(). ...

    Again it's not a big deal to emit it my self. That's all for now, thank you very much for your help.

    PS : Should the documentation contain an example or be more specific about the whole looping thing for a QMovie?


  • Qt Champions 2017

    Hi
    Animated gifs have done that since the beginning of time (almost) but yeah
    would be nice if Docs mentioned that it does honor the loop count in the header, but provides no api to control it :)



  • @mrjj If someone wants to propose something for the Documentation, he/she must report at https://bugreports.qt.io/ or there is nothing that can be done?


  • Qt Champions 2017

    @Petross404_Petros-S
    That would be the correct place as it also accepts feature requests and
    documents updates.



  • @mrjj Great, thanks!


  • Qt Champions 2017

    @Petross404_Petros-S
    Btw, i assumed you need to use various GIFs you cant know before hand
    as else i would just have suggested to open the GIF in online editor and set loop count to 1
    and it would only play once.



  • @mrjj

    i would just have suggested to open the GIF in online editor and set loop count to 1

    I had done exactly that at https://ezgif.com/, before JKSH wrote the post about frameChanged :)

    I find it a cleaner solution to control the loop with the Qt API (even with bloated -if that's the word- code), rather that duck the issue with external tools. It's just me, it might be better to do this without all that trouble.


  • Qt Champions 2017

    @Petross404_Petros-S
    Hehe funny enough thats the site i would have suggested :)
    Yeah fixing it with code is better than fixing one file.
    Then if GIF changes it still works.



  • @mrjj You wrote exactly what I was thinking but couldn't express. Anyway, I am writing a bug report, I hope for the best.


  • Moderators

    @Petross404_Petros-S said in About QMovie : play gif animation only once:

    *frameCount returns 20 for the specific gif file, but for some reason I must stop at frame #19.

    If frameCount() == 20, then currentFrameNumber() ranges from 0 to 19. It's like an array: If an array has 20 elements, then its last valid index is 19.

    Nonetheless, even if you used == movie->frameCount(), I'd expect your movie to just loop forever instead of crash. Check your stack trace; there might be other issues in your application.

    ** What boggled my mind is that QMovie::stop() doesn't emit finished as I expected from the description in Detailed Description.

    This sounds like a bug. I verified (with Qt 5.10.0 MSVC 2015 x86) that it does emit stateChanged(QMovie::NotRunning) but doesn't emit finished(). Could you please file a separate bug report for this?

    Please post the links to both reports (documentation and missing finished() signal) here, thanks!



  • @JKSH Sure I will. I didn't know that not emitting finished is a bug (although I suspected it) and I mentioned it in the documentation bug report too. I will report the missing signal tomorrow morning.

    Also, the whole frameCount - 1 thing reminded me the arrays and now you confirmed it. I will retry to crash it and see what you are talking about. Thank you all for the support. :)



  • Good morning (or whatever in your local time zone is), here is the bug report for the missing signal.


Log in to reply
 

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