(QT C++) Animate GIF Based on Slider/Dial Value - c++

I am relatively new to Qt and I have a question that seems simple enough.
How could I make a slider/line edit thingy that when set to a certain value (0) it displays the beginning of a GIF animation, then when set to a higher value (12000) it goes to a frame further in the animation? The GIF image would be next to the slider, not a separate window. Pretty much any way to do this is accepted.
:)
Thanks

You can use QMovie to operate animated GIF image. You need to set QMovie::CacheAll cache mode in order to make rewinding backwards possible. Put a slider and a label in your form. Add QMovie* movie private class member.
In the constructor:
movie = new QMovie("c:/tmp/sample.gif", "GIF", this);
movie->setCacheMode(QMovie::CacheAll);
ui->slider->setRange(0, movie->frameCount() - 1);
on_slider_valueChanged(0);
In the slot:
void MainWindow::on_slider_valueChanged(int value) {
movie->jumpToFrame(value);
ui->label->setPixmap(movie->currentPixmap());
}

Related

How to do overlapping custom items in qt?

Like in the title. I have few objects, and i wanted to set graphics to them and overlap each other. I'm setting 18 squares (terrains). I'm adding to this terrains graphics:
setPixmap(QPixmap(":Graphics/Terrain")); like so.
Then i want to add frame to it. I have 4 files of format .png, every one is 200x200px. First one is terrain.png - it's a green square, second and third are frames, about 20px wide with a transparent center (with alpha channel) and the last .png file is City.png, in left corner of City.png, there is a small graphics, rest is transparent (alpha channel). When i'm trying to set the frame by the same way as terrain.png:
setPixmap(QPixmap(":Graphics/Frame"));
it covers all of the terrain.png graphics, despite the fact that the center of frame1.png is transparent.
Furthermore i want to add some others object like for example City.png. How to do it? Mayby exists some diffrent way except using this setPixmap function ?
The setPixmap approach seems to work quite nicely, when used like this:
QGraphicsScene* scene = new QGraphicsScene();
QGraphicsPixmapItem* itemA = new QGraphicsPixmapItem();
itemA->setPixmap(QPixmap(":/NonTransparentImage.png"));
scene->addItem(itemA);
QGraphicsPixmapItem* itemB = new QGraphicsPixmapItem();
itemB->setPixmap(QPixmap(":/TransparentImage.png"));
scene->addItem(itemB);
QGraphicsView* view = new QGraphicsView(scene);
view->show();
As you expect, the transparent itemB overlaps itemA.

QT Changing pixmap each frame

I just started working in QT, and I am trying to change some pixel colors of pixmap each frame.
QGraphicsScene *graphic = new QGraphicsScene(this);
QPixmap LifeMap=QPixmap(QPixmap::fromImage(LifeGrid));
this is my graphic scene and my pixmap declaration.
So in a function that i call each frame i use this line of code to update pixmap
graphic->addPixmap(QPixmap::fromImage(LifeGrid));
and it workes, but every few seconds i have less and less ram memory. After my memory is full my computer stopes working and i cant do anything.
(i guess it has something to do with addPixmap function, because it declars a whole new pixmap)
So is there a function that would allow me just to update this graphic, or change pixmap?
Thanks in advance!
Is there a function that would allow me just to update this graphic, or change pixmap?
Yes. You have to use a single pixmap item instead of creating a new one each time the pixmap changes. You can also hold on to the scene and the item by value, saving on an extra layer of indirection and leveraging the C++ compiler to do all the memory management for you automatically:
class MyClass : ... {
QGraphicsScene m_scene;
QGraphicsPixmapItem m_pixmapItem; // must be after m_scene: in C++ this order matters!
...
}
MyClass::MyClass() {
...
m_scene->addItem(&m_pixmapItem);
}
void MyClass::myMethod() {
m_pixmapItem.setPixmap(QPixmap::fromImage(LifeGrid));
}

How to get control back after drawing on scene

I am trying to implement simple backtracking algorythm (solitaire game) and I also want to show the progress, not only the answer. So I decided to use QGraphicsView to draw gamefield's status on it. I want to update scene after I field's changd and when I want to get control back, to resume changing gamefield. What is the best way to do this?
UPD:
pseudocode
List<Step>* solve(GameField& field) {
//changing gamefield, clearing scene, adding some objects (rects with text) to scene
updateScene(); //here I want the scene to be updated right now, and
//not when it's scheduled to
}

How to show part of an image using QT?

So, this is my problem: I have this very big image, and I want to show only a specific part of it. After the user pressing a specific key I want the image to move, showing another part of it. The transition from one part of the image to another have to be smooth, animated.
I tried using a QLabel to show the image but it always shows the center of the image, and I do not really know how to make the animation. What would you guys suggest?
Interesting question. Here is something I just tested and seems to work.
Add a QGraphicsView with dimensions the dimensions of the part of the image you want to display, eg 100x100. Create a QGraphicsScene and add it to the view:
QGraphicsScene* pScene = new QGraphicsScene(this);
ui->graphicsView->setScene(pScene);
Now add your image into the scene. In my case I has an image in my resource file. The trick is to set the sceneRect to the position you want to display. I wanted to display an 100x100 part of the image starting from 0,300 :
pItem = pScene->addPixmap(QPixmap::fromImage(QImage(":/photos/image")));
pScene->setSceneRect(0,300,100,100);
In order to test the smooth moving I added a button which when clicked is triggering a slot called move. This slot simply updates the sceneRect. In my simple example I just move the image 100 pixels right. In a real world scenario you could also move it diagonally or vertically and check the image limits.
void move()
{
for (unsigned i=currentX; i<currentX + 100; i++)
{
ui->graphicsView->scene()->setSceneRect(i,300,100,100);
qApp->processEvents();
}
currentX += 100;
}
Notice the currentX variable. It is nothing more than the last image position. Also we must call the processEvents in order to "see" the image moving smoothly.
You could use QPixmap::copy( int x, int y, int width, int height ) to copy a region of the image and display that.
Few options:
Try using a Q3CanvasSprite (within in a Q3Canvas). It is designed more for splitting one image into multiple ones but not for animating between them. You could try abusing it and declaring (say) 100 frames (10 per digit, which would be used as animation steps) or just use the move() method.
Try QGraphicsPixmapItem::setOffset() (within a QGraphicsScene). This might be overkill as QGraphicsScene is made for large number of images).
I'm not sure, but maybe this can be done with QStateMachine and QAbstractAnimation.

Tiling with QGraphicsScene and QGraphicsView

I'm creating a image visualizer that open large images(2gb+) in Qt.
I'm doing this by breaking the large image into several tiles of 512X512. I then load a QGraphicsScene of the original image size and use addPixmap to add each tile onto the QGraphic Scene. So ultimately it looks like a huge image to the end user when in fact it is a continuous array of smaller images stuck together on the scene.First of is this a good approach?
Trying to load all the tiles onto the scene takes up a lot of memory. So I'm thinking of only loading the tiles that are visible in the view. I've already managed to subclass QGraphicsScene and override its drag event thus enabling me to know which tiles need to be loaded next based on movement. My problem is tracking movement on the scrollbars. Is there any way I can create an event that get called every time the scrollbar moves. Subclassing QGraphicsView in not an option.
QGraphicsScene is smart enough not to render what isn't visible, so here's what you need to do:
Instead of loading and adding pixmaps, add classes that wrap the pixmap, and only load it when they are first rendered. (Computer scientists like to call this a "proxy pattern"). You could then unload the pixmap based on a timer. (They would be transparently re-loaded if unloaded too soon.) You could even notify this proxy path of the current zoom level, so that it loads lower resolution images when they will be rendered smaller.
Edit: here's some code to get you started. Note that everything that QGraphicsScene draws is a QGraphicsItem, (if you call ::addPixmap, it's converted to a ...GraphicsItem behind the scenes), so that's what you want to subclass:
(I haven't even compiled this, so "caveat lector", but it's doing the right thing ;)
class MyPixmap: public QGraphicsItem{
public:
// make sure to set `item` to nullptr in the constructor
MyPixmap()
: QGraphicsItem(...), item(nullptr){
}
// you will need to add a destructor
// (and probably a copy constructor and assignment operator)
QRectF boundingRect() const{
// return the size
return QRectF( ... );
}
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget){
if(nullptr == item){
// load item:
item = new QGraphicsPixmapItem( ... );
}
item->paint(painter, option, widget);
}
private:
// you'll probably want to store information about where you're
// going to load the pixmap from, too
QGraphicsPixmapItem *item;
};
then you can add your pixmaps to the QGraphicsScene using QGraphicsScene::addItem(...)
Although an answer has already been chosen, I'd like to express my opinion.
I don't like the selected answer, especially because of that usage of timers. A timer to unload the pixmaps? Say that the user actually wants to take a good look at the image, and after a couple of seconds - bam, the image is unloaded, he will have to do something in order the image to reappear. Or may be you will put another timer, that loads the pixmaps after another couple of seconds? Or you will check among your thousand of items if they are visible? Not only is this very very irritating and wrong, but that means that your program will be using resources all the time. Say the user minimizes you program and plays a movie, he will wonder why on earth my movie is freezing every couple of seconds...
Well, if I misunderstood the proposed idea of using timers, execuse me.
Actually the idea that mmutz suggested is better. It reminded me of the Mandelbrot example. Take a look at it. Instead of calculating what to draw you can rewrite this part to loading that part of the image that you need to show.
In conclusion I will propose another solution using QGraphicsView in a much simpler way:
1) check the size of the image without loading the image (use QImageReader)
2) make your scene's size equal to that of the image
3) instead of using pixmap items reimplement the DrawBackground() function. One of the parameters will give you the new exposed rectangle - meaning that if the user scrolls just a little bit, you will load and draw only this new part(to load only part of an image use setClipRect() and then read() methods of the QImageReader class). If there are some transformations you can get them from the other parameter(which is QPainter) and apply them to the image before you draw it.
In my opinion the best solution will be to combine my solution with the threading shown in the Mandelbrot example.
The only problem that I can think of now is if the user zooms out with a big scale factor. Then you will need a lot of resources for some time to load and scale a huge image. Well I see now that there is some function of the QImageReader that I haven't tried yet - setScaledSize(), which maybe do just what we need - if you set a scale size and then load the image maybe it won't load first the entire image – try it. Another way is just to limit the scale factor, a thing that you should do anyway if you stick to the method with the pixmap items.
Hope this helps.
Unless you absolutely need the view to be a QGraphicsView (e.g. because you place other objects on top of the large background pixmap), I'd really recommend just subclassing QAbstractScrollArea and reimplementing scrollContentsBy() and paintEvent().
Add in a LRU cache of pixmaps (see QPixmapCache for inspiration, though that one is global), and make the paintEvent() pull used pixmaps to the front, and be set.
If this sounds like more work than the QGraphicsItem, believe me, it's not :)