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.
Related
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++
I'm making an endless running game in which users dodge obstacles, and I'm working on producing the obstacles right now. The plan I had where I'm spawning these obstacles is as follows:
obstacle->setPosition(CCPointMake(this->getContentSize().width, this->getContentSize().height*.75));
obstacle->setScale(.5);
this->addChild(obstacle);
_obstacles->addObject(obstacle);
obstacle->runAction(CCMoveBy::create(2.0, CCPointMake(-(this->getContentSize().width + obstacle->getContentSize().width/2), 0)));
obstacle->removeFromParent();
I set the position, set it's scale, add it to the scene, run an action on it so that it moves across the screen from right to left, add it to an array called _obstacles to be used elsewhere, and then I remove it from the scene so as to save memory. However, the problem is that once I try implement this, the obstacle doesn't show up at all as if it's nowhere to be seen. When I don't call obstacle->removeFromParent() it shows up and performs the action. What am I doing wrong here? If I don't call removeFromParent(), what do I call? Is there a problem in my code not related to removeFromParent()?
The reason that obstacle doesn't apeear at all is that it is removed as it start moving. You just have to create a sequence of move action and function call with obstacle as parameter and than remove this obstacle in that function , so that obstacle will be removed after moving out of screen.
CCCallFuncN *myCallFunc = CCCallFuncN::create(this, callfuncN_selector(CLASS_NAME::removeObstacles));
obstacle->runAction(CCSequence::create(CCMoveBy::create(2.0, CCPointMake(-(this->getContentSize().width + obstacle->getContentSize().width/2), 0)),myCallFunc,NULL));
Method to remove obstacle from array and from parent view
void CLASS_NAME::removeObstacles(CCObject* pSender){
// Type cast pSender to obstacle type e.g if obstacle is of CCSprite type.
CCSprite *tempObstacle = (CCSprite *)pSender;
_obstacle.pop_back(tempObstacle);
tempObstacle->removeFromParent();
}
Don't forget to replace CLASS_NAME by your class name
I would need more info on what removeFromParent is doing and how you use _obstacles
I'm taking a guess on how the parent is structured. I would guess that it has a list of children and when the parent is updated/render, it calls those functions for its children. If removeFromParent removes the object from its parent list, then that would explain why it is not being render or used in the scene. If you have a setup like this, then you should only call removeFromParent before destroying the parent object
I have a simple CCScene containing only one node created from a CocosBuilder template with [CCBReader nodeGraphWithFile:] method.
So far, I did not release the ccb node in the dealloc method of the scene because I expected it to be autoreleased. But in the allocation profiler, I noticed that there is a memory leak if I push/pop the scene several times in the CCDirector.
This memory leak disappears if I actually release the node in the scene's dealloc method.
Why do I need to release the node though I didn't retain/init it ? Is there something I misunderstood ?
What happens to the object created through this?
[CCBReader nodeGraphWithFile:]
If you assign it to a retain property, it will get retained; so you need to release it explicitly. E.g.:
self.nodeGraph = [CCBReader nodeGraphWithFile:...];
if nodeGraph is declared as a retain property, the autoreleased object created in [CCBReader nodeGraphWithFile:] will get retained by the property and you will need to release it in dealloc.
Contrast this to not using a property to keep a reference to the node object and add it directly to the node hierarchy:
[self addChildNode:[CCBReader nodeGraphWithFile:...]];
in this case, you would not need doing any explicit release, since you are not retaining yourself the object.
I am developing a GUI which will allow the user to modify ellipses using mouse/QSpinBox events on top of a background picture.
I want to set it up so that when a user clicks on an ellipse, the ellipse changes color and has been "selected."
I am using QGraphicsView/Scene with QGraphicsEllipseItem. Here in lies my problem, the setPen(QPen & const) call is a reference thus:
If I allocate a pen on the stack and pass it in, I get a segfault.
If I allocate a pen on the heap, it doesn't get deleted when I set a new pen
The accessor method returns a copy of the pen
Anyways, I must be approaching this wrong, could any offer any suggestions?
P.S. - I would like to avoid making yet ANOTHER member variable. I am going to have many ellipses, each one should not have its own pen variable!
This code will cause a segfault:
void MyClass::SetupEllipses()
{
QPen pen();
pen.setColor(QColor(255,0,0));
pen.setWidth(2);
m_ellipse = new QGraphicsItemEllipse(); //This is a member variable of MyClass
m_ellipse->setRect(some ssize here);
m_ellipse->setPen(pen);
m_graphicsview->scene()->addItem(m_ellipse); //m_graphicsview is also a member variable of MyClass and has had a scene added to it.
}
If I allocate a pen on the stack and pass it in, I get a segfault.
That's the right way - if you get a segfault, that's certainly not due to the QPen.
Post your code and debugger stack trace.
Each QGraphicsEllipseItem will store a (light-weight) copy of the QPen anyway, so creating QPen instances on the heap is nonsensical and error-prone.
Do not worry about too many QPen instances. QPen uses implicit sharing, i.e. if you share the same unmodified pen, the copies are cheap.
I'm experimenting with the QT library and QGraphicsScene.
I can add my own objects to the scene and all that is fine.
What I would like to have now are some lists outside QGraphicsScene of the objects.
Let's say Squares, Circles and Triangles.
They all live in the scene, and are (if I'm correct) copied and owned by the scene when I add them.
The question is: what kind of list template containers would be best for implementing my outside lists.
I guess they would need to be pointers so I figured boost::ptr_container.
I know I would need a system to make sure the lists are in sync with the scene once I start deleting items. that means removing the pointer form the lists just before removing the object from the scene.
Any ideas on how i should build this system would be very nice.
You don't need it, Qt does everything for you.
Once you add an item into QGraphicsScene,the scene takes ownership of the item. Which means, the scene is now responsible for storing the item, and freeing the memory occupied by the item when it's needed (usually when you delete an item manually with deleteItem(), or when the scene itself is deleted).
Whenever you want you can ask for the list of all items that have been added to the scene by calling the items() function:
MyGraphicScene.items();
This function returns a list of pointers to all of the scene's items as a QList< QGraphicsItem * >, which is exactly what you need.