Russian Qt Forum
Ноябрь 23, 2024, 16:08 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: Как реализовать анимацию gif-картинки на сцене?  (Прочитано 10524 раз)
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?
« Последнее редактирование: Февраль 20, 2010, 22:05 от Greep » Записан
SABROG
Гость
« Ответ #1 : Февраль 20, 2010, 23:25 »

ППС: неужели QMovie можно использовать только в связке с QLabel?

Плохо читал документацию. Используй сигнал void QMovie::frameChanged ( int frameNumber )   [signal], в слоте вызывай update(), в paint() вызывай QPixmap QMovie::currentPixmap () const
Записан
Greep
Гость
« Ответ #2 : Февраль 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'
Записан
SABROG
Гость
« Ответ #3 : Февраль 21, 2010, 02:08 »

А я и не писал, что нужно добавлять слоты в QGraphicsItem. Добавлять нужно в класс, который управляет всей сценой и QMovie привязывать к конкретному итему, а уж как это реализовать думай сам.
Записан
Greep
Гость
« Ответ #4 : Февраль 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()));
« Последнее редактирование: Февраль 21, 2010, 03:16 от Greep » Записан
SABROG
Гость
« Ответ #5 : Февраль 21, 2010, 14:59 »

Код:
connect(img->getMovie(), SIGNAL(frameChanged(int)), scene, SLOT(update()));

Обновлять сцену целиком конечно оверхед. У каждого QGraphicsItem'a есть собственный метод update(). Просто нужно ловить сигналы frameChanged() от каждого экземпляра QMovie в одном слоте. Можно использовать для этого класс QSignalMapper, из которого получать сигнал с параметрами, например, указатель на QMovie и QGraphicsItem который нужно обновить.
Записан
Greep
Гость
« Ответ #6 : Февраль 21, 2010, 15:28 »

В QSignalMapper нельзя передать указатель на класс Image т.к. он не является потомком ни QWidget-a ни QObject-a...
ПС: если я правильно понял семантику использования QSignalMapper-а

upd:
я думаю оптимальным было бы сделать слот update() в классе Image, но как я ни пытался, у меня не получается сделать двойное наследование от QObject-a и QGraphicsItem-a
« Последнее редактирование: Февраль 21, 2010, 15:30 от Greep » Записан
SABROG
Гость
« Ответ #7 : Февраль 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
Записан
Greep
Гость
« Ответ #8 : Февраль 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();
}

Блин, ну не может же быть всё так сложно >_<
Записан
SABROG
Гость
« Ответ #9 : Февраль 22, 2010, 00:39 »

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

Ну попробуй в QGraphicsPixmapItem впихни .gif, правда я не думаю, что будет анимация. Можно еще попробовать QLabel добавить на сцену, только имхо это оверхед.
Записан
Greep
Гость
« Ответ #10 : Февраль 22, 2010, 00:50 »

Как ни крути оверхед получается.
Спасибо за помощь =)
Записан
SABROG
Гость
« Ответ #11 : Февраль 22, 2010, 23:15 »

Как вариант можно попробовать замутить через QGraphicsEffect
Придется его унаследовать и сделать практически тоже самое что и в QGraphicsItem::paint(). Зато здесь есть слот update() к которому можно подвязать сигнал frameChanged(). В общем-то в этом случае можно аггрегировать QMovie в QGraphicsEffect, таким образом у тебя весь функционал будет в одном классе и каждый экземпляр QGraphicsEffect будет содержать свою анимацию, которую можно включить и выключить в любое время, соответственно уже можно будет использовать голый QGraphicsItem или любой другой класс на его базе. Например вывод прозрачной анимации на какой-нибудь QWidget на сцене.
Записан
BiTOk
Гость
« Ответ #12 : Май 08, 2010, 12:29 »

Тоже столкнулся с этой проблемой. Попробовал реализовать так:
QMovie проигрывает gifanim. frameChanged соединил со слотом, в котором написал
Код:
    worker->setPixmap(pixmap);
    worker->update();
Работает пару секунд и падает с сегфолтом. Собственно, почему? Могу прогу выложить.
Записан
p166
Гость
« Ответ #13 : Май 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)
     }
 }
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.05 секунд. Запросов: 20.