Results 1 to 6 of 6

Thread: Problem with removing QLabel wrapped in a std::shared_ptr from QGraphicsScene

  1. #1
    Join Date
    Dec 2016
    Posts
    7
    Thanks
    4
    Qt products
    Qt5
    Platforms
    Windows

    Default Problem with removing QLabel wrapped in a std::shared_ptr from QGraphicsScene

    Hello everybody. I have a very strange behaviour with QLabel and QGraphicsScene.
    I develop a simple game, where 2 players how a set of soldiers and basically try to eliminate each other. This is a turn-based strategy and obviously I use QLabel to display QTimer value on a game map.
    The aim of developing this game is learning of Qt, c++11 and some programming techniques.

    I'm gonna highlight troubled parts with comments containing "ololo"
    . So here is a code:
    Qt Code:
    1. std::shared_ptr<GameManager> GameManagerCreator::createGameManager()
    2. {
    3. // ololo if remove static everything is fine.
    4. static std::shared_ptr<GameManager> gameManager(new GameManager());
    5. gameManager->init();
    6. qDebug() << "GameManagerCreator::createGameManager use count " << gameManager.use_count();
    7. return gameManager;
    8. }
    To copy to clipboard, switch view to plain text mode 

    This is a creation of my game logic class. As u can see, I would like to use here a singleton.

    Qt Code:
    1. void GameManager::initialiseGameInterface()
    2. {
    3. mp_menuButton.reset(new QPushButton);
    4. setButtonSettings(mp_menuButton, ":/settingImages/menuButton.png");
    5. mp_scene->addWidget(mp_menuButton.get())->setZValue(1.0);
    6. const std::shared_ptr<Tile> rightTopCorner = mp_scene->tile(std::make_pair(0, mp_scene->mapSizeInTiles().second - 1));
    7. mp_menuButton->move(rightTopCorner->rect().x(), rightTopCorner->rect().y());
    8.  
    9. mp_changeTurnButton.reset(new QPushButton);
    10. setButtonSettings(mp_changeTurnButton, ":/settingImages/changeTurnButton.png");
    11. mp_scene->addWidget(mp_changeTurnButton.get())->setZValue(1.0);
    12. connect(mp_changeTurnButton.get(), SIGNAL(clicked(bool)), this, SLOT(changeTurn()));
    13.  
    14. QString settingsFolder(":/settingImages/");
    15. mp_moveBlantButton.reset(new QPushButton);
    16. setButtonSettings(mp_moveBlantButton, settingsFolder + "moveButton.png");
    17. mp_scene->addWidget(mp_moveBlantButton.get())->setZValue(1.0);
    18. connect(mp_moveBlantButton.get(), SIGNAL(clicked(bool)), this, SLOT(moveButtonClicked()));
    19.  
    20. mp_attackBlantButton.reset(new QPushButton);
    21. setButtonSettings(mp_attackBlantButton);
    22. mp_scene->addWidget(mp_attackBlantButton.get())->setZValue(1.0);
    23. connect(mp_attackBlantButton.get(), SIGNAL(clicked(bool)), this, SLOT(attackButtonClicked()));
    24.  
    25. mp_meleeAttackBlantButton.reset(new QPushButton);
    26. setButtonSettings(mp_meleeAttackBlantButton, settingsFolder + "meleeAttackButton.png");
    27. mp_scene->addWidget(mp_meleeAttackBlantButton.get())->setZValue(1.0);
    28. connect(mp_meleeAttackBlantButton.get(), SIGNAL(clicked(bool)), this, SLOT(meleeAttackButtonClicked()));
    29.  
    30. mp_timerDisplayer.reset(new QLabel);
    31. setTimerSettings();
    32. // ololo this line is leading to a crash in the destructor!!!
    33. mp_scene->addWidget(mp_timerDisplayer.get())->setZValue(1.0);
    34. placeTimerDisplayer();
    35. }
    To copy to clipboard, switch view to plain text mode 

    Here I initialize interface settings (mostly buttons). And look at the line
    Qt Code:
    1. mp_scene->addWidget(mp_timerDisplayer.get())->setZValue(1.0);
    To copy to clipboard, switch view to plain text mode 
    This one leads to an exception in the destructor. I can't understand why. There is no difference between initializing buttons and this QLabel. However if I comment this line everything works perfectly except there is no QLabel on the map but no surprise here

    Qt Code:
    1. void GameManager::cleanScene()
    2. {
    3. QList<QGraphicsItem*> list =mp_scene->items();
    4. for(int i=0;i<list.length();i++)
    5. mp_scene->removeItem(list[i]);
    6. }
    7.  
    8. void GameManager::clean()
    9. {
    10. cleanScene();
    11.  
    12. // cleaning other things
    13.  
    14.  
    15. if(mp_playerInCharge)
    16. mp_playerInCharge.reset();
    17. if(mp_menuButton)
    18. mp_menuButton.reset();
    19. if(mp_moveBlantButton)
    20. mp_moveBlantButton.reset();
    21. if(mp_attackBlantButton)
    22. mp_attackBlantButton.reset();
    23. if(mp_meleeAttackBlantButton)
    24. mp_meleeAttackBlantButton.reset();
    25. if(mp_changeTurnButton)
    26. mp_changeTurnButton.reset();
    27.  
    28. if(mp_turnTimer)
    29. mp_turnTimer.reset();
    30. if(mp_timerDisplayer) {
    31. //ololo crashes here
    32. mp_timerDisplayer.reset();
    33. }
    34.  
    35. if(mp_view) {
    36. mp_view.reset();
    37. }
    38. if(mp_scene) {
    39. mp_scene.reset();
    40. }
    41. }
    42.  
    43. GameManager::~GameManager()
    44. {
    45. clean();
    46. qDebug() << "gamemanager destructor";
    47. }
    To copy to clipboard, switch view to plain text mode 


    And here is my destructor. In case you wondering why I don't use mp_scene->clear(), that's because if I have mp_timerDisplayer it crashes. So before deleting all my interface buttons and so on, I remove them from the scene.
    The most strange part that everything works perfectly if i don't use singleton. (simply remove static from
    Qt Code:
    1. static std::shared_ptr<GameManager> gameManager(new GameManager());
    To copy to clipboard, switch view to plain text mode 
    )

    Another strange thing is that if I leave static be and remove this QLabel which displays timer count down everything also works fine.

    It's worth mentioning that all other code which is used with mp_timerDisplayer is strictly about styles and setting values, I don't create it anywhere else and I don't delete it anywhere else.
    If it's any help I can publish it as well.
    The error is access violation and bla bla bla. Looks like I delete the same object twice. First it deleted then I remove it from the scene and the second one is when std::shared_ptr counter goes to zero in mp_timerDisplayer.reset();

    But you see there is no difference between button creations and freeing and this QLabel.

    Any help would be much appreciated.

  2. #2
    Join Date
    Dec 2016
    Posts
    7
    Thanks
    4
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: Problem with removing QLabel wrapped in a std::shared_ptr from QGraphicsScene

    Other facts popped up during the investigation.
    First of all, my buttons become visible when you click any soldier. So at the start of the game no buttons visible and QLabel is visible. So if I make it invisible there are no problems during destruction. At the same time if I close the app when any buttons are visible then my app is going down on the first button in destructor.

  3. #3
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    4,686
    Thanks
    257
    Thanked 748 Times in 738 Posts
    Qt products
    Qt5
    Platforms
    Windows Android

    Default Re: Problem with removing QLabel wrapped in a std::shared_ptr from QGraphicsScene

    Why are you wrapping your QLabel instance in an std::shared_ptr? QObject instances (of which your QLabel is one) are always owned by their parent QObject. This parent controls the lifetimes of its QObject children. By wrapping the QLabel instance in an std::shared_ptr you are setting up a conflict over ownership, because std::shared_ptr also controls the lifetime of the pointer it wraps. You can't have two owners controlling the lifetime of the same instance. It sounds like an error in your program design if you think you need to pass around a QLabel pointer via std::shared_ptr.

    In particular, if your QLabel's QWidget owner causes the QLabel instance to go out of scope and be deleted, the std::share_ptr will be left holding an invalid dangling pointer to freed memory since there is way for it to know of the deletion. If the shared_ptr deletes the QLabel instance first, then the QWidget parent probably will get notified, since the QLabel will emit a signal as it is being destroyed.
    <=== The Great Pumpkin says ===>
    Please use CODE tags when posting source code so it is more readable. Click "Go Advanced" and then the "#" icon to insert the tags. Paste your code between them.

  4. The following user says thank you to d_stranz for this useful post:

    monnzz (17th January 2017)

  5. #4
    Join Date
    Dec 2016
    Posts
    7
    Thanks
    4
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: Problem with removing QLabel wrapped in a std::shared_ptr from QGraphicsScene

    First of all, I would like to thank you for your reply

    I use std::shared_ptr just to practice new features of C++11. I thought about this
    QObject instances (of which your QLabel is one) are always owned by their parent QObject
    , but i thought I needed explicitly provide relationship parent-child by passing a parent to a child constructor or using setParent function, and I'm not doing any of those things so as to prevent the ownership of QLabel or QPushButton by any other object except std::shared_ptr.
    Qt Code:
    1. mp_timerDisplayer.reset(new QLabel /* no parent here */);
    To copy to clipboard, switch view to plain text mode 

    In my header I declare these members as follows

    Qt Code:
    1. class GameManger : public QObject, public enable_shared_from_this
    2. {
    3. private:
    4. // some other declarations
    5. std::shared_ptr<QLabel*> mp_timerDisplayer;
    6. std::shared_ptr<QPushButton*> mp_menuButton;
    7. // some other declarations
    8. }
    To copy to clipboard, switch view to plain text mode 

    Now, regarding QGraphicsScene and its ownership over passed objects. Before deleting objects I remove them from the scene by calling this function:

    Qt Code:
    1. void GameManager::cleanScene()
    2. {
    3. QList<QGraphicsItem*> list =mp_scene->items();
    4. for(int i=0;i<list.length();i++)
    5. mp_scene->removeItem(list[i]);
    6. }
    To copy to clipboard, switch view to plain text mode 

    It said that removeItem does the next things
    Removes the item item and all its children from the scene. The ownership of item is passed on to the caller (i.e., QGraphicsScene will no longer delete item when destroyed).
    So in my view, I have only one object which controls the lifetime of QLabel here and it is std::shared_ptr.

    If I'm wrong could you please point this out?

    The most strange thing that if I remove static from std::shared_ptr in GameManagerCreator everything works like a charm.

  6. #5
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,419
    Thanks
    37
    Thanked 1,546 Times in 1,496 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Problem with removing QLabel wrapped in a std::shared_ptr from QGraphicsScene

    Since the static will be destroyed after main() has ended, the widgets could have been deleted by some hook in QApplication's destructor.

    Or the label's destructor accesses some part of QApplication which is already gone.

    Have you tried making the QApplication object shared-pointered static as well?

    Cheers,
    _

  7. The following user says thank you to anda_skoa for this useful post:

    monnzz (17th January 2017)

  8. #6
    Join Date
    Dec 2016
    Posts
    7
    Thanks
    4
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: Problem with removing QLabel wrapped in a std::shared_ptr from QGraphicsScene

    anda_skoa, thank you very much, that is the answer! It was enough to make QApplication static, that's it

Similar Threads

  1. Send shared_ptr using signal
    By theZivkovic in forum Newbie
    Replies: 1
    Last Post: 18th September 2015, 20:48
  2. Using std::shared_ptr for Qt objects
    By 8Observer8 in forum Newbie
    Replies: 2
    Last Post: 18th June 2014, 17:02
  3. QSharedPointer vs Boost::tr1::shared_ptr()
    By photo_tom in forum Qt Programming
    Replies: 1
    Last Post: 11th March 2010, 16:48
  4. Add QLabel to QGraphicsScene
    By Qt Coder in forum Newbie
    Replies: 2
    Last Post: 19th March 2009, 06:21
  5. Removing and Redrawing Lines in a QGraphicsScene in PyQT
    By giverson in forum Qt Programming
    Replies: 1
    Last Post: 26th February 2007, 20:23

Tags for this Thread

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.