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++
Related
I have some problem. I need to read SVG files and place content on the QGraphicsView scene. I read documentation about QSvgWidget and QSvgRenderer, but don't understand how get all shapes from file, point coordinates, where place they and have a posibillity to change color, size and other properties. Who can suggest methods, how I can do this? Thanks in advance!
I tried create QSvgWidget and I managed draw one figure with his color, but if figures more than one, they all dye in black color, and I don't know, how I can control this.
ui->setupUi(this);
scene = new QGraphicsScene;
scene->setSceneRect(-200, -100, 400, 200);
svg_widget = new QSvgWidget(":/resources/Test3.svg");
svg_widget->renderer();
svg_rend = new QSvgRenderer;
painter = new QPainter;
svg_rend->render(painter);
svg_widget->render(painter);
scene->addWidget(svg_widget);
ui->graphicsView->setScene(scene);
I understood that after creating QSvgWidget and call renderer(), QSvgRenderer does not affect on work with SVG. Can I combine svg_rend with svg_widget? Or does it make no sense?
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));
}
In the constructor of a QMainWindow...
A new circle is created on the heap and passed to the scene, the circle shows up, everythin is working:
QGraphicsScene * scene = new QGraphicsScene(this);
CustomGraphicsView * view = new CustomGraphicsView(scene,this);
QGraphicsEllipseItem * ellipse = new QGraphicsEllipseItem (100,100,30,30);
ellipse->setPen(QPen(Qt::green,10));
scene->addItem(ellipse);
Now we create circle on the stack and pass it by reference. But this time the cricle will never shows up:
QGraphicsScene * scene = new QGraphicsScene(this);
CustomGraphicsView * view = new CustomGraphicsView(scene,this);
QGraphicsEllipseItem ellipse(100,100,30,30);
ellipse.setPen(QPen(Qt::green,10));
scene->addItem(&ellipse);
I guess it gets destroyed before it shows up. But I don't understand why? Why is this not working and what is the rule behind this behaviour?
From http://doc.qt.io/qt-4.8/qgraphicsscene.html#addItem
Adds or moves the item and all its childen to this scene. This scene takes ownership of the item.
A QGraphicsScene cannot take ownership of an item if it is created on the stack. This implies that the item has to be created on the heap.
QGraphicsEllipseItem * ellipse = new QGraphicsEllipseItem (100,100,30,30);
ellipse->setPen(QPen(Qt::green,10));
scene->addItem(ellipse);
This works because the scene object is in charge of deleteing the object which you've allocated via new. Somewhere in the depths of the Qt framework, there will be a call along the lines of:
delete item;
Now with this code:
QGraphicsEllipseItem ellipse(100,100,30,30);
ellipse.setPen(QPen(Qt::green,10));
scene->addItem(&ellipse);
Consider what happens. The delete item; is still there, but it will be applied not only on a pointer to a local object but (probably, depending on the program flow) also to a pointer to an object which no longer lives. The code which performs the delete simply has no idea that the object was not created via new.
Any attempt to destroy an object more than once is undefined behaviour, as is trying to use an object which was already destroyed. Your question is a good example of why passing around pointers to local objects should be treated as an especially dangerous action which requires extra caution.
By the way:
But this time the cricle will never shows up:
That's a mere coincidence. Undefined behaviour means that everything can happen.
I am making use of a QGraphicsScene and I am adding regular widgets (QLineEdit, QComboBox, etc.) to it via implicitly created QGraphicsProxyWidget objects:
m_pLineEdit = new QLineEdit("", 0);
m_pProxy = m_pGraphicsScene->addWidget(m_pLineEdit);
I am currently searching for a way to later retrieve those widgets from the scene again for processing, but am not able to find one.
I tried the following approaches already:
Since I cannot pass the graphics scene as parent to the widget constructor, retrieving the widgets via m_pGraphicsScene->findChildren(QLineEdit*) does not work, since there is no direct relation.
The graphics scene does have a QGraphicsSceneBspTreeIndex child, but that is not part of the official Qt API and therefore relying on it cannot be the way to go.
Bottom-line: How can I get all the QGraphicsProxyWidget objects from a Qt graphics scene? Can this be done in the Qt standard or do I have to subclass QGraphicsScene and try to manage the widgets myself?
Bottom-line: How can I get all the QGraphicsProxyWidget objects from a Qt graphics scene?
Get the list of all the items in the scene via scene->items() , then check if they're of the right class:
// Horrible C++98 code which doesn't even feature algorithms
QList<QGraphicsItem *> items = scene->items();
foreach (QGraphicsItem *item, items) {
QGraphicsProxyWidget *w;
if (w = qgraphicsitem_cast<QGraphicsProxyWidget *>(item)) {
use(w);
}
}
However, I'd like to stress that you should really keep track of the items you put in the scene. (At least, the ones you're interested in using afterwards). Walking over the scene and retrieving the items like this seems very fragile, and it's a signal of poor code quality and poor design. Note that you have the proxy returned by the addWidget call, just save it somewhere.
Just after posting the question I by chance found a solution in the Qt source code. The widget proxies are treated as regular QGraphicsItem internally and can be casted via qgraphicsitem_cast:
QList<QGraphicsItem*> graphicsItemList = m_pGraphicsScene->items();
foreach(QGraphicsItem* pGraphicsItems, graphicsItemList)
{
QGraphicsProxyWidget* pProxy = qgraphicsitem_cast<QGraphicsProxyWidget*>(pGraphicsItems);
if(pProxy)
{
QLineEdit* pLineEdit = qobject_cast<QLineEdit*>(pProxy->widget());
if(pLineEdit)
// do stuff
}
}
If someone knows a simpler/faster method, I'd be happy to hear about it. Until then I will use the approach outlined above.
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());
}