Qt Forum

    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    • Unsolved

    Update: Forum Guidelines & Code of Conduct

    Solved Animated GIF in QGraphicsScene/QGraphicsView

    General and Desktop
    qgraphicsscene qgraphicsview qmovie gif animations
    3
    8
    2799
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • artwaw
      artwaw last edited by

      Good morning,
      I have QGraphicsScene in QGraphicsView to display and manipulate images. It works well, no issues here. It is the first time I tried QGraphicsScene instead if QLabel and I am very fond of how neatly it all works.
      However I stumbled upon the edge case where animated gif is selected for display - as expected, only the first frame is displayed.
      I tried usual way of having QMovie assigned to QLabel etc. - added that to the scene with addWidget() method but label is not visible.
      This project is built using Qt5.15.2 (macOS clang/win mingw8)

      So the question: is there any sane way of making it work without me having to rewrite half of the Qt ;) or should I just let go the idea of displaying animated gifs in the scene?

      As usual, many thanks in advance.
      Artur

      For more information please re-read.

      Kind Regards,
      Artur

      JonB 1 Reply Last reply Reply Quote 0
      • JonB
        JonB @artwaw last edited by JonB

        @artwaw
        I would search for what others have done to get this working. For example
        https://forum.qt.io/topic/15658/solved-how-to-play-gif-animation-in-qgraphicsview-widget
        https://www.qtcentre.org/threads/61528-How-to-place-animated-GIF-on-QGraphicsScene
        https://stackoverflow.com/a/21083803/489865

        I think the last one at least is doing it via QMovie/QLabel/QGraphicsScene::addWidget(). So don't know why you seem to say you can't.

        artwaw 1 Reply Last reply Reply Quote 2
        • JonB
          JonB @artwaw last edited by JonB

          @artwaw
          I would search for what others have done to get this working. For example
          https://forum.qt.io/topic/15658/solved-how-to-play-gif-animation-in-qgraphicsview-widget
          https://www.qtcentre.org/threads/61528-How-to-place-animated-GIF-on-QGraphicsScene
          https://stackoverflow.com/a/21083803/489865

          I think the last one at least is doing it via QMovie/QLabel/QGraphicsScene::addWidget(). So don't know why you seem to say you can't.

          artwaw 1 Reply Last reply Reply Quote 2
          • artwaw
            artwaw @JonB last edited by

            @JonB Thanks. This didn't popup (first result seems the way to go) when I used search in the forum O_O

            For more information please re-read.

            Kind Regards,
            Artur

            1 Reply Last reply Reply Quote 0
            • Chris Kawa
              Chris Kawa Moderators last edited by Chris Kawa

              You could certainly use a label, but proxy widgets are a bit heavy. All you really need to do is paint a frame of QMovie, so you can very easily make a custom graphics item that does that. You just need to implement 3 simple methods and you should be good to go, e.g.

              class GraphicsMovieItem : public QGraphicsItem
              {
              public:
                  using QGraphicsItem::QGraphicsItem;
              
                  void setMovie(QMovie* movie)
                  {
                      prepareGeometryChange();
                      QObject::disconnect(mConnection); // disconnect old object
                      mMovie = movie;
                      if (mMovie)
                          mConnection = QObject::connect(mMovie, &QMovie::frameChanged, [=]{ update(); });
                  }
              
                  QRectF boundingRect() const override
                  {
                      if (mMovie)
                          return mMovie->frameRect();
                      else
                          return QRectF();
                  }
              
                  void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override
                  {
                      if (mMovie)
                          painter->drawPixmap(mMovie->frameRect(), mMovie->currentPixmap(), mMovie->frameRect());
                  }
              
              private:
                  QPointer<QMovie> mMovie;
                  QMetaObject::Connection mConnection;
              };
              
              artwaw 2 Replies Last reply Reply Quote 3
              • artwaw
                artwaw @Chris Kawa last edited by

                @Chris-Kawa That's smart! I'll definitely try this approach out, thank you!

                For more information please re-read.

                Kind Regards,
                Artur

                1 Reply Last reply Reply Quote 0
                • artwaw
                  artwaw @Chris Kawa last edited by

                  @Chris-Kawa I am sorry to bother you but example code provided raises some issues during the compilation:

                  /Users/arturwawrowski/cpp/build-TNImageViewer-Desktop_Qt_5_15_2_clang_64bit-Debug/moc_qgraphicsmovieitem.cpp:67: error: no member named 'staticMetaObject' in 'QGraphicsItem'; did you mean simply 'staticMetaObject'?
                  moc_qgraphicsmovieitem.cpp:67:34: error: no member named 'staticMetaObject' in 'QGraphicsItem'; did you mean simply 'staticMetaObject'?
                      QMetaObject::SuperData::link<QGraphicsItem::staticMetaObject>(),
                                                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                                                   staticMetaObject
                  moc_qgraphicsmovieitem.cpp:66:58: note: 'staticMetaObject' declared here
                  QT_INIT_METAOBJECT const QMetaObject QGraphicsMovieItem::staticMetaObject = { {
                                                                           ^
                  

                  and several of:

                  /Users/arturwawrowski/cpp/build-TNImageViewer-Desktop_Qt_5_15_2_clang_64bit-Debug/moc_qgraphicsmovieitem.cpp:78: error: invalid use of non-static data member 'd_ptr'
                  moc_qgraphicsmovieitem.cpp:78:21: error: invalid use of non-static data member 'd_ptr'
                      return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
                             ~~~~~~~~~^~~~~
                  

                  intertwined with:

                  /Users/arturwawrowski/cpp/build-TNImageViewer-Desktop_Qt_5_15_2_clang_64bit-Debug/moc_qgraphicsmovieitem.cpp:78: error: 'd_ptr' is a protected member of 'QObject'
                  moc_qgraphicsmovieitem.cpp:78:21: error: 'd_ptr' is a protected member of 'QObject'
                  ../../Qt/5.15.2/clang_64/lib/QtCore.framework/Headers/qobject.h:450:33: note: declared protected here
                      QScopedPointer<QObjectData> d_ptr;
                                                  ^
                  

                  Code:
                  header

                  #ifndef QGRAPHICSMOVIEITEM_H
                  #define QGRAPHICSMOVIEITEM_H
                  
                  #include <QGraphicsItem>
                  #include <QObject>
                  #include <QMovie>
                  #include <QPainter>
                  
                  class QGraphicsMovieItem : public QGraphicsItem
                  {
                      Q_OBJECT
                  public:
                      QGraphicsMovieItem(QGraphicsItem *parent = nullptr);
                      void setMovie(QMovie* movie);
                      QRectF boundingRect() const override;
                      void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override;
                  
                  private:
                      QPointer<QMovie> mMovie;
                      QMetaObject::Connection mConnection;
                  };
                  
                  #endif // QGRAPHICSMOVIEITEM_H
                  

                  implementation:

                  #include "qgraphicsmovieitem.h"
                  
                  QGraphicsMovieItem::QGraphicsMovieItem(QGraphicsItem *parent) : QGraphicsItem(parent) {}
                  
                  void QGraphicsMovieItem::setMovie(QMovie* movie) {
                      prepareGeometryChange();
                      QObject::disconnect(mConnection);
                      mMovie = movie;
                      if (mMovie) {
                          mConnection = QObject::connect(mMovie, &QMovie::frameChanged, [=]{ update(); });
                      }
                  }
                  
                  QRectF QGraphicsMovieItem::boundingRect() const {
                      if (mMovie) { return mMovie->frameRect(); }
                      else { return QRectF(); }
                  }
                  
                  void QGraphicsMovieItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
                      Q_UNUSED(option)
                      Q_UNUSED(widget)
                      if (mMovie) {
                          painter->drawPixmap(mMovie->frameRect(), mMovie->currentPixmap(), mMovie->frameRect());
                  
                      }
                  }
                  

                  What I did wrong this time?

                  For more information please re-read.

                  Kind Regards,
                  Artur

                  1 Reply Last reply Reply Quote 0
                  • Chris Kawa
                    Chris Kawa Moderators last edited by Chris Kawa

                    QGraphicsItem does not inherit QObject so don't use Q_OBJECT for your class.

                    Btw. modern C++ has this wonderful thing called inherited constructors, so you don't have to write the boilerplate empty constructors that just pass parameters to base classes. Instead of writing QGraphicsMovieItem(QGraphicsItem *parent = nullptr); and implementing it as an empty method you can just write using QGraphicsItem::QGraphicsItem; and it does exactly the same thing. Super useful when base class has multiple constructors with different parameters as you can inherit them all in just that one line.

                    artwaw 1 Reply Last reply Reply Quote 2
                    • artwaw
                      artwaw @Chris Kawa last edited by

                      @Chris-Kawa Noted thank you. Now it compiles without errors. I think I need to read a bit about newer c++...

                      For more information please re-read.

                      Kind Regards,
                      Artur

                      1 Reply Last reply Reply Quote 1
                      • First post
                        Last post