Russian Qt Forum

Qt => 2D и 3D графика => Тема начата: Greep от Февраль 20, 2010, 21:54



Название: Как реализовать анимацию gif-картинки на сцене?
Отправлено: Greep от Февраль 20, 2010, 21:54
Есть класс, наследуемый от QGraphicsItem:
Код:
class Image : public QGraphicsItem
{
public:
    Image(const QString &fileName);
    void paint(QPainter *painter,
               const QStyleOptionGraphicsItem *option, QWidget *widget);

protected:
    void mousePressEvent(QGraphicsSceneMouseEvent *event);

private:
    QImage image;
};

Код:
Image::Image(const QString &fileName)
{
    image = QImage(fileName);
}

void Image::paint(QPainter *painter,
             const QStyleOptionGraphicsItem */*option*/, QWidget */*widget*/)
{
    painter->drawImage(0, 0, image);
    painter->setRenderHint(QPainter::Antialiasing, true);
}

void Image::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    //do something
    QGraphicsItem::mousePressEvent(event);
}

Этот код отображает на сцену только первый (если я не ошибаюсь) кадр из гифки.
Собственно сабж: как сделать анимацию?

ПС: пробовал что-то сделать через QMovie и QImageReader - ничего не вышло.
ППС: неужели QMovie можно использовать только в связке с QLabel?


Название: Re: Как реализовать анимацию gif-картинки на сцене?
Отправлено: SABROG от Февраль 20, 2010, 23:25
ППС: неужели QMovie можно использовать только в связке с QLabel?

Плохо читал документацию. Используй сигнал void QMovie::frameChanged ( int frameNumber )   [signal], в слоте вызывай update(), в paint() вызывай QPixmap QMovie::currentPixmap () const


Название: Re: Как реализовать анимацию gif-картинки на сц
Отправлено: Greep от Февраль 21, 2010, 01:22
Спасибо за совет. Но при попытке следовать этому совету появилась необходимость добавить слот в класс Image, но это оказалось не так просто. QGraphicsItem не является потомком QObject, поэтому просто приписав макрос Q_OBJECT сигналов\слотов не получить. Если сделать множественное наследование от QObject и QGraphicsItem, то компилятор ругается на то, что методы класса QGraphicsItem недоступны из класса Image.
Цитировать
~/mainwindow.cpp:88: error: 'QGraphicsItem' is not an accessible base of 'Image'


Название: Re: Как реализовать анимацию gif-картинки на сцене?
Отправлено: SABROG от Февраль 21, 2010, 02:08
А я и не писал, что нужно добавлять слоты в QGraphicsItem. Добавлять нужно в класс, который управляет всей сценой и QMovie привязывать к конкретному итему, а уж как это реализовать думай сам.


Название: Re: Как реализовать анимацию gif-картинки на сц
Отправлено: Greep от Февраль 21, 2010, 02:12
А я и не писал, что нужно добавлять слоты в QGraphicsItem. Добавлять нужно в класс, который управляет всей сценой и QMovie привязывать к конкретному итему, а уж как это реализовать думай сам.
т.е. при каждой смене кадра в анимации нужно будет обновлять всю сцену? О_о
А если на сцене сотня gif-ок, в которых кадры меняются с интервалом 0 мс?

Upd:
оказывается у сцены есть слот update(QRectF);
Upd2:
но это ничего не меняет :(
Upd3:
кое-что получилось, изменённый код:
Код:
class Image : public QGraphicsItem
{
public:
    Image(const QString &fileName);
    void paint(QPainter *painter,
               const QStyleOptionGraphicsItem *option, QWidget *widget);
    const QMovie *getMovie();

protected:
    void mousePressEvent(QGraphicsSceneMouseEvent *event);

private:
    QMovie *movie;
};

Код:
Image::Image(const QString &fileName)
{
    movie = new QMovie(fileName);
    movie->start();
}

void Image::paint(QPainter *painter,
             const QStyleOptionGraphicsItem */*option*/, QWidget */*widget*/)
{
    painter->drawPixmap(0, 0, movie->currentPixmap());
    painter->setRenderHint(QPainter::Antialiasing, true);
}

void Image::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    //do something
    QGraphicsItem::mousePressEvent(event);
}

ну и соответственно при создании объекта класса Image делается коннект:
Код:
Image *img = new Image(fileName);
connect(img->getMovie(), SIGNAL(frameChanged(int)), scene, SLOT(update()));


Название: Re: Как реализовать анимацию gif-картинки на сц
Отправлено: SABROG от Февраль 21, 2010, 14:59
Код:
connect(img->getMovie(), SIGNAL(frameChanged(int)), scene, SLOT(update()));

Обновлять сцену целиком конечно оверхед. У каждого QGraphicsItem'a есть собственный метод update(). Просто нужно ловить сигналы frameChanged() от каждого экземпляра QMovie в одном слоте. Можно использовать для этого класс QSignalMapper, из которого получать сигнал с параметрами, например, указатель на QMovie и QGraphicsItem который нужно обновить.


Название: Re: Как реализовать анимацию gif-картинки на сц
Отправлено: Greep от Февраль 21, 2010, 15:28
В QSignalMapper нельзя передать указатель на класс Image т.к. он не является потомком ни QWidget-a ни QObject-a...
ПС: если я правильно понял семантику использования QSignalMapper-а

upd:
я думаю оптимальным было бы сделать слот update() в классе Image, но как я ни пытался, у меня не получается сделать двойное наследование от QObject-a и QGraphicsItem-a


Название: Re: Как реализовать анимацию gif-картинки на сцене?
Отправлено: SABROG от Февраль 21, 2010, 17:04
Зато можно присвоить ID

void QSignalMapper::setMapping ( QObject * sender, int id )

В int можно запихнуть как индекс какого-нибудь списка, так и сам указатель, просто reinterpret_cast<>() придется вызывать.
Или idшник можно впихнуть в QGraphicsItem: void QGraphicsItem::setData ( int key, const QVariant & value )

Потом пробегаться по всем итемам на сцене и искать нужный ID: QList<QGraphicsItem *> QGraphicsScene::items () const


Название: Re: Как реализовать анимацию gif-картинки на сц
Отправлено: Greep от Февраль 21, 2010, 18:52
Кое что получилось:
Код:
class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    MainWindow(QWidget *parent = 0);
    ...
private:
    QSignalMapper *signalMapper;
    QList<Image *> imageList;
    ...
private slots:
    void addImage(const QString &fileName);
    void updateItem(int i);
    ...
}

Код:
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    ...
    signalMapper = new QSignalMapper(this);
    ...
}

void MainWindow::addImage(const QString &fileName)
{
    Image *img = new Image(fileName);
    imageList.append(img);
    connect(img->getMovie(), SIGNAL(frameChanged(int)), signalMapper, SLOT(map()));
    signalMapper->setMapping(img->getMovie(), imageList.indexOf(img));
    connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(updateItem(int)));
}

void MainWindow::updateImage(int i)
{
    imageList.at(i)->update();
}

Блин, ну не может же быть всё так сложно >_<


Название: Re: Как реализовать анимацию gif-картинки на сц
Отправлено: SABROG от Февраль 22, 2010, 00:39
Блин, ну не может же быть всё так сложно >_<

Ну попробуй в QGraphicsPixmapItem впихни .gif, правда я не думаю, что будет анимация. Можно еще попробовать QLabel добавить на сцену, только имхо это оверхед.


Название: Re: Как реализовать анимацию gif-картинки на сцене?
Отправлено: Greep от Февраль 22, 2010, 00:50
Как ни крути оверхед получается.
Спасибо за помощь =)


Название: Re: Как реализовать анимацию gif-картинки на сцене?
Отправлено: SABROG от Февраль 22, 2010, 23:15
Как вариант можно попробовать замутить через QGraphicsEffect (http://qt.nokia.com/doc/4.6/qgraphicseffect.html)
Придется его унаследовать и сделать практически тоже самое что и в QGraphicsItem::paint(). Зато здесь есть слот update() к которому можно подвязать сигнал frameChanged(). В общем-то в этом случае можно аггрегировать QMovie в QGraphicsEffect, таким образом у тебя весь функционал будет в одном классе и каждый экземпляр QGraphicsEffect будет содержать свою анимацию, которую можно включить и выключить в любое время, соответственно уже можно будет использовать голый QGraphicsItem или любой другой класс на его базе. Например вывод прозрачной анимации на какой-нибудь QWidget на сцене.


Название: Re: Как реализовать анимацию gif-картинки на сцене?
Отправлено: BiTOk от Май 08, 2010, 12:29
Тоже столкнулся с этой проблемой. Попробовал реализовать так:
QMovie проигрывает gifanim. frameChanged соединил со слотом, в котором написал
Код:
    worker->setPixmap(pixmap);
    worker->update();
Работает пару секунд и падает с сегфолтом. Собственно, почему? Могу прогу выложить.


Название: Re: Как реализовать анимацию gif-картинки на сцене?
Отправлено: p166 от Май 20, 2010, 16:00
Мне кажется такие вещи много проще делать через QML.

QML AnimatedImage Element Reference

Properties
currentFrame : int
frameCount : int
paused : bool
playing : bool

Detailed Description
This item provides for playing animations stored as images containing a series of frames, such as GIF files. The full list of supported formats can be determined with QMovie::supportedFormats().

 Item {
     width: anim.width; height: anim.height+8
     AnimatedImage { id: anim; source: "pics/games-anim.gif" }
     Rectangle { color: "red"; width: 4; height: 8; y: anim.height
         x: (anim.width-width)*anim.currentFrame/(anim.frameCount-1)
     }
 }