Results 1 to 9 of 9

Thread: closing parent widget during QMenu::exec()

  1. #1
    Join Date
    Jan 2014
    Posts
    6
    Qt products
    Qt5
    Platforms
    Windows

    Default closing parent widget during QMenu::exec()

    I have an open QDialog and create a popup menu and show it with QMenu::exec().
    Qt Code:
    1. QMenu *menu=new QMenu(mydialog);
    2. ...
    3. QAction *ret=menu->exec();
    4. delete menu;
    To copy to clipboard, switch view to plain text mode 
    On a Mac it is possible to close the dialog while the menu is open. The menu is closed and destroyed automatically since it is a child of the dialog. However, QMenu::exec() returns after dialog and menu have been destroyed, and the line "delete menu" in the above code creates a crash. Unter Windows this situation never appears because clicking on the dialog's close button closes the menu first and only a second click closes the dialog.

    1) Can I just omit the line "delete menu"? Would it create a memory leak if the menu is closed regularly?
    2) Is there some way to determine if a QObject still exists or is already destroyed?
    Last edited by hartmut; 30th January 2016 at 23:31. Reason: typo in title

  2. #2
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,230
    Thanks
    302
    Thanked 864 Times in 851 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: closing parent widget during QMenu::exec()

    1) Can I just omit the line "delete menu"? Would it create a memory leak if the menu is closed regularly?
    You can omit the line. Since you create the menu with the dialog as its parent, it will be deleted when the dialog is deleted. With QObject-based classes, it is better to call "deleteLater()" instead of delete to ensure that Qt can clean things up properly.

    2) Is there some way to determine if a QObject still exists or is already destroyed?
    Connect a slot to QObject::destroyed().

    You don't show enough code to know how you are creating your QAction instances. If these are created as children of the QMenu instance, then deleting the QMenu would also cause them to be destroyed and you'd end up with an invalid pointer as the returned QAction.

    Sounds like you are trying to reinvent a wheel though. QWidget already supports context menus. You simply add QAction instances using QWidget::addAction() and call QWidget::setContextMenuPolicy() with Qt::DefaultContextMenu. Connect your QAction::triggered() signals to the slots you want executed. Qt will automatically create, pop up, and hide the contect menu on a right-mouse press.

  3. #3
    Join Date
    Jan 2014
    Posts
    6
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: closing parent widget during QMenu::exec()

    If I don't delete the menu at all and call it several times before I close the dialog, then all menu instances are kept in memory until I close the dialog, right?
    But it seems to work with the following code
    Qt Code:
    1. QMenu *menu=new QMenu(mydialog);
    2. ...
    3. menu->deleteLater();
    4. QAction *ret=menu->exec();
    To copy to clipboard, switch view to plain text mode 
    The menu is called if a button is pressed and is displayed below the button. It works more or less like a Combobox but with the possibility of submenus. The menu content depends on other dialog features. Therefore I create the menu right before it is displayed.
    The QActions are children of the menu. After "QAction *act=menu->exec()" I use "act->data().toInt()" to determine the selection and afterwards I would delete the menu.
    If the dialog is closed while the menu is open, then menu->exec() returns NULL which I take care of anyway.

  4. #4
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: closing parent widget during QMenu::exec()

    Quote Originally Posted by hartmut View Post
    1) Can I just omit the line "delete menu"? Would it create a memory leak if the menu is closed regularly?
    The menu would be deleted when the dialog is deleted.
    You could end up with multple menu instance until that happens.

    To avoid that you could keep the menu in a member variable and only create it the first time it is used.

    Quote Originally Posted by hartmut View Post
    2) Is there some way to determine if a QObject still exists or is already destroyed?
    Easily done using QPointer:
    Qt Code:
    1. QPointer<QMenu> menu(new QMenu(mydialog));
    2.  
    3. menu->exec();
    4.  
    5. delete menu.data();
    To copy to clipboard, switch view to plain text mode 
    QPointer is a smart pointer for tracking QObjects. It gets reset to 0 if the object is deleted.

    Quote Originally Posted by hartmut View Post
    If I don't delete the menu at all and call it several times before I close the dialog, then all menu instances are kept in memory until I close the dialog, right?
    Yes

    Quote Originally Posted by hartmut View Post
    But it seems to work with the following code
    Qt Code:
    1. QMenu *menu=new QMenu(mydialog);
    2. ...
    3. menu->deleteLater();
    4. QAction *ret=menu->exec();
    To copy to clipboard, switch view to plain text mode 
    That could delete the menu while it is opened.
    deleteLater() creates a "delete event" and puts it into the event queue.
    QMenu::exec() processes events.

    Quote Originally Posted by hartmut View Post
    The menu is called if a button is pressed and is displayed below the button.
    QPushButton::setMenu()?

    Cheers,
    _

  5. #5
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,230
    Thanks
    302
    Thanked 864 Times in 851 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: closing parent widget during QMenu::exec()

    Qt Code:
    1. QMenu *menu=new QMenu(mydialog);
    2. ...
    3. menu->deleteLater();
    4. QAction *ret=menu->exec();
    To copy to clipboard, switch view to plain text mode 
    As anda_skoa says, it works by accident. I am surprised it doesn't blow up, but maybe the QMenu::exec() is running its own event loop so the deleteLater() call isn't processed until the menu is closed. Lucky for you.

    If you are creating the actions dynamically each time the button is clicked, then why create the QMenu instance on the heap at all? (i.e, using operator new()). Instead, create it on the stack and it will automatically go out of scope (and be cleanly destroyed) when the button's clicked slot exits:

    Qt Code:
    1. void MyDialog::onButtonClicked()
    2. {
    3. QMenu menu( mydialog );
    4. // ... add actions
    5. QAction * ret = menu.exec();
    6. // do something with the action
    7. } // menu goes out of scope
    To copy to clipboard, switch view to plain text mode 

  6. #6
    Join Date
    Jan 2014
    Posts
    6
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: closing parent widget during QMenu::exec()

    Creating the menu on the stack "QMenu menu( mydialog );" does not work. The program crashes immediately if the dialog is closed during menu.exec(). The call stack shows "QWidget::~QWidget()" -> "QObjectPrivate::deleteChildren()" -> "free".
    Creating the menu on the stack with "QMenu menu( 0 );" does not crash but the menu remains open while the dialog is closed, which is not what we want.

    Now I use the trick with QPointer<QMenu>. This works perfectly. Thank you all for your help.

  7. #7
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,230
    Thanks
    302
    Thanked 864 Times in 851 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: closing parent widget during QMenu::exec()

    Why would you want to close the dialog that owns the menu which you are executing an action owned by the menu? The QPointer<QMenu> "trick" is exactly that - in this case it looks like a kludge that works around code that likely deletes QWidget instances inappropriately. Yeah, maybe it works, but it probably isn't actually a good solution to the problem.

    It is generally bad in Qt to delete your parent object. If the ownership hierarchy is dialog -> menu -> actions, and the triggered() slot for an action results in the dialog being deleted, then that's a fragile piece of code. The safer way would be to make the QAction instances children of whatever owns the dialog - something further up the hierarchy of ownership that stays in scope once the dialog is destroyed. That way, executing the triggered() slot doesn't result in trying to delete the object that owns the action.

  8. #8
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: closing parent widget during QMenu::exec()

    Quote Originally Posted by d_stranz View Post
    Why would you want to close the dialog that owns the menu which you are executing an action owned by the menu?
    In this case it is out of the control of the application if I understand hartmut correctly.

    It can also happen in other cases, when some application logic deletes windows/dialogs based on external events (e.g. timer, network) while some internals of the window/dialog is spinning a nested event loop.

    Creating a modal dialog or menu on the stack only works if you don't pass a parent (which you usually want to do to have window relationship set up correctly) or you are absolutely sure that no code path exists that can delete the window/dialog.

    Using QPointer, on the other hand, works in all cases.

    Quote Originally Posted by d_stranz View Post
    The QPointer<QMenu> "trick" is exactly that - in this case it looks like a kludge that works around code that likely deletes QWidget instances inappropriately. Yeah, maybe it works, but it probably isn't actually a good solution to the problem.
    It actually is. It is even recommended in e.g. KDE's guidelines, because it happens a lot that external events (in the case of KDE application things like D-Bus messages, but more generically network events or timers) can lead to things being deleted.

    See e.g. https://blogs.kde.org/2009/03/26/how...d-how-fix-it-0 for one analysis of the problem.

    Cheers,
    _

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

    d_stranz (31st January 2016)

  10. #9
    Join Date
    Jan 2014
    Posts
    6
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: closing parent widget during QMenu::exec()

    Exactly, it is not me who closes the dialog. On a Mac (I have OS X 10.11.2) it is just possible for the user to click on the red [x] in the top left corner of the dialog while the menu is open. Then QMenu::exec() returns after everything has been destroyed. So I have no choice but to deal with it.
    I was surprised myself, because if I try the same thing under Windows, only the menu disappears but the dialog remains open. Once the menu is closed I have to click a second time on the dialog's close button to really close it.

Similar Threads

  1. Replies: 1
    Last Post: 11th March 2011, 20:34
  2. QMenu exec under MacOSX
    By yxtx1984 in forum Qt Programming
    Replies: 0
    Last Post: 16th December 2010, 06:13
  3. How to make QMenu.exec() return a QWidgetAction
    By gregsan in forum Qt Programming
    Replies: 5
    Last Post: 15th April 2010, 01:04
  4. Replies: 0
    Last Post: 16th March 2010, 13:24
  5. Replies: 7
    Last Post: 14th January 2010, 09:47

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.