简体   繁体   中英

Navigate among GIFs with QMovie and signals/slots in Qt C++

I'm using Qt QMovie to play a collection of GIFs. On reaching the last frame of a GIF, I disconnect the signal, stop the QMovie and start the next QMovie. Until this step, the code works fine.

After this, I introduce next and previous buttons that can navigate across the GIFs. The onClick action of these buttons does the same actions as above, except instead of waiting for the last frame, I disconnect and stop the movie immediately.

This randomly causes all the GIFs to play faster than normal. And also, the upcoming GIFs navigates to the next gif abruptly even before the end of frame is reached.

This effect cascades exponentially consuming lot of memory. Finally ends up crashing the app.

Here is my header file

class AnimationBox : public QWidget
{
    Q_OBJECT

public:
    explicit AnimationBox(QWidget *parent = 0);
    ~AnimationBox();

private slots:
    void loopAnimation(int frame);
    void changeSlide(int direction = 1);
    void prevGIFSlot();
    void nextGIFSlot();

private:
    Ui::AnimationBox *ui;
    std::vector<QMovie*> movieVector;  
    int currentMovieIndex;
};

Here is my cpp file

AnimationBox::AnimationBox(QWidget *parent) 
: QWidget(parent)
, ui(new Ui::AnimationBox),
{
    ui->setupUi(this);

    currentMovieIndex = 0;
    movieVector.push_back(new QMovie("/Users/qq/Desktop/gif1.gif"));
    movieVector.push_back(new QMovie("/Users/qq/Desktop/gif2.gif"));
    movieVector.push_back(new QMovie("/Users/qq/Desktop/gif3.gif"));

    ui->movieLabel->setMovie(movieVector[currentMovieIndex]);
    movieVector[currentMovieIndex]->start();

    connect(movieVector[currentMovieIndex], SIGNAL(frameChanged(int)), this, SLOT(loopAnimation(int)));
    connect(ui->leftButton, SIGNAL(clicked()), this, SLOT(prevGIFSlot()));
    connect(ui->rightButton, SIGNAL(clicked()), this, SLOT(nextGIFSlot()));

}

void AnimationBox::loopAnimation(int frame)
{    
    if (frame == movieVector[currentMovieIndex]->frameCount() - 1)
    {
        disconnect(movieVector[currentMovieIndex], SIGNAL(frameChanged(int)), this, SLOT(loopAnimation(int)));
        movieVector[currentMovieIndex]->stop();
        changeSlide();
    }
}

void AnimationBox::prevGIFSlot()
{
    disconnect(movieVector[currentMovieIndex], SIGNAL(frameChanged(int)), this, SLOT(loopAnimation(int)));
    movieVector[currentMovieIndex]->stop();        
    changeSlide(-1);
}

void AnimationBox::nextGIFSlot()
{
    disconnect(movieVector[currentMovieIndex], SIGNAL(frameChanged(int)), this, SLOT(loopAnimation(int)));
    movieVector[currentMovieIndex]->stop();        
    changeSlide(1);
}

void AnimationBox::changeSlide(int direction)
{
    currentMovieIndex = (currentMovieIndex + direction) % movieVector.size();
    ui->movieLabel->setMovie(movieVector[currentMovieIndex]);
    movieVector[currentMovieIndex]->start();

    connect(movieVector[currentMovieIndex], SIGNAL(frameChanged(int)), this, SLOT(loopAnimation(int)));
}

And also, the upcoming GIFs navigates to the next gif abruptly even before the end of frame is reached.

This is because the slot loopAnimation(int frame) is called once that last frame is loaded, and your slot stops the movie .. early enough before the frame itself is played , so it gets chopped ... this slight change shows you how that could be avoided:

void AnimationBox::loopAnimation(int frame)
{
    if (frame == 0)
    {
        disconnect(movieVector[currentMovieIndex], SIGNAL(frameChanged(int)), this, SLOT(loopAnimation(int)));
        movieVector[currentMovieIndex]->stop();
        changeSlide();
    }
}

Note that this is a conditional solution because you actually start the movie before connecting to slot .. so its not hit when frame is 0 initially, but it gets hit at next animation cycle, but that next animation is stopped here. if you change your code stuff .. this may not be best solution .. but just make sure last frame is played before stopping the movie.

This randomly causes all the GIFs to play faster than normal.

This is not reproducible! I can run your code without seeing this issue ... which could mean it might be related to to your UI design of the label .. or anything in the GIFs themselves ... try to bring your UI settings to default if you have any other settings we don't see in the code. Bottom line: it works normally here.

2 comments:

  • Better to use Qt new Signal/Slot syntax:

    connect(ui->leftButton, &QPushButton::clicked, this, &AnimationBox::prevGIFSlot);

  • a good approach is to place your signal/slot connect() before you start the logic of the object (in your case starting the movie), in your case you start the movie .. then .. you establish the signal/slot connections .. I believe this is not a good practice.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM