QT Changing pixmap each frame - c++

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));
}

Related

Qt | QPixmap scaling incorrectly

I have a QPixmap and I set it to resize according to ratio as the window is resized. When the images is first loaded in, it is good and clear for that ratio, but as I resize the image, it distorts all of it.
It manages to go from this:
to this:
Relevant Code:
void MainWindow::resizeEvent(QResizeEvent *)
{
QPixmap pix = ui->labelImage->pixmap()->scaled(ui->labelImage->size(),
Qt::KeepAspectRatio);
ui->labelImage->setPixmap(pix);
}
You're reading the current pixmap from the widget, scaling it, and then writing the scaled pixmap to the widget. Consider what happens if the widget becomes very small and is then resized to something much larger -- you'll get a lot of artifacts due to the scaling transformation used.
I think a better approach would be to store the original full size image in a member of your MainWindow class, say...
QPixmap m_original_pixmap;
Then use a scaled version of that in your resizeEvent member...
void MainWindow::resizeEvent(QResizeEvent *)
{
QPixmap pix = m_original_pixmap.scaled(ui->labelImage->size(), Qt::KeepAspectRatio);
ui->labelImage->setPixmap(pix);
}
Not sure if that will clear everything up but should go some way to removing some of the artifacts.
As a side note, if you're concerned about image quality you might consider specifying Qt::SmoothTransformation as the pixmap transformation mode in the scaling operation.

Qt QGgraphicsView Fix ram usage

I'm using QGraphicsView to load images using this statement:
if (QFile().exists(pl)){
Sleft = new QGraphicsScene;
Sleft->addPixmap(pl);
ui->left->setScene(Sleft);
ui->left->show();
ui->left->update();
}
if (QFile().exists(pr) && (pr != "")){
Sright = new QGraphicsScene;
Sright->addPixmap(pr);
ui->right->setScene(Sright);
ui->right->show();
ui->right->update();
}
else {
scene3 = new QGraphicsScene;
ui->right->setScene(scene3);
ui->right->show();
}
Sleft, Sright and scene3 are declared as
QGraphicsScene* Sleft;
QGraphicsScene* Sright;
QGraphicsScene* scene3;
I'm trying to figure out why the RAM usage keeps increasing and how to solve it. I think it might have something to do with me reinitalizing the variable every time the method is called.
The problem is that you create a new QGraphicsScene and a new pixmap item every time you load an image, but you don't destroy the old scene and the old pixmap item.
It's not necessary to create a new scene every time you load an image. You can create and set one QGraphicsScene for each QGraphicsView at startup.
When you want to load a new image, you can use QGraphicsScene::addPixmap and save the returned QGraphicsPixmapItem pointer into a class variable. If an image is already loaded, you must first destoy it using delete.
I suggest you to learn more about memory allocation in C++

How to draw to "parent TLW backing store"?

This might be very complicated question not many people know answer but I still will ask.
I do have QWindow derived class, with overloaded event(), which uses Backing store and fill in whole window with a colour, let say black.
Now I have my QT QML app, when I create my window and set parent as main view of my app, I getting window sized to 1x1px ! This is driving me crazy..
I dug though the QT source code and found this:
void QQnxRasterWindow::adjustBufferSize()
{
// When having a raster window we don't need any buffers, since
// Qt will draw to the parent TLW backing store.
const QSize windowSize = window()->parent() ? QSize(1,1) : window()->size();
if (windowSize != bufferSize())
setBufferSize(windowSize);
}
void QQnxRasterWindow::setParent(const QPlatformWindow *wnd)
{
QQnxWindow::setParent(wnd);
adjustBufferSize();
}
Which is kinda bummer because I have no idea how I suppose to use TLW and draw into my window now.
Any ideas?
First of all what is TLW ?
Second how do I draw into parent TLW in a way it will end up in my window buffer.
Thank you
QT 5.3.1
Edit:
not renderNow() - my mistake,
overloaded function event, which uses event UpdateRequested to draw my background.
Edit2:
Also this is only problem when I set parent, when no parent set I can do whatewer I want with my QWindow and it has own buffer. Kinda weird.

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

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());
}

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 :)