qtuser-interfaceqdialogqmenu

How to programmatically close QMenu


I have pretty specific situation. I want to place a QAction into QToolbar and reach following behaviour:

  1. Checkable QAction with icon.
  2. Classic arrow on the right side which is used for showing menu
  3. By pressing this arrow my QDialog should appears on screen instead of QMenu-like one

Now I'm a bit confused with implementing all this things together.

For now I've created QAction added it to toolbar and also created an empty QMenubecause I didn't get the idea of how to add the "dropdown" arrow another way.
So, I also connected my slot to QMenu aboutToShow() signal and now, I can create my dialog and exec() it just before QMenu shows. But here's the main problem appears: after I did everything with my dialog an click OK button QMenu trying to appear, but as it is empty it shows nothing and further actions become available only after I left-click somwhere to "close" this menu.

Is there any way to force QMenu not to show or can inherit from QMenu and reimplemnt its behaviour (I've tried to do such trick with exec() show() popup() methods of QMenu after subclassing from it, but none of them are being called when menu appears on the screen) ?


Solution

  • Here's the solution, which worked for me.

    class QCustomMenu : public QMenu
    {
      Q_OBJECT
    public:
      QCustomMenu(QObject *parent = 0):QMenu(parent){};
    };
    

    In code:

    QAction* myActionWithMenu = new QAction ( "ActionText", toolbar);
    QCustomMenu* myMenu = new QCustomMenu(toolbar);
    connect(myMenu, SIGNAL(aboutToShow()), this, SLOT(execMyMenu()));
    

    execMyMenu() implementation:

    void execMyMenu(){
      m_activeMenu = (QCustomMenu*)sender(); // m_activeMenu -- private member of your head class, needs to point to active custom menu
      QMyDialog* dlg = new QMyDialog();
      // setup your dialog with needed information
      dlg->exec();
      // handle return information
      m_myTimer = startTimer(10); // m_myTimer -- private member of your head(MainWindow or smth like that) class
    }
    

    Now we have to handle timerEvent and close our menu:

    void MyHeadClass::timerEvent(QTimerEvent *event)
    {
        // Check if it is our "empty"-menu timer 
        if ( event->timerId()==m_myTimer )
        {
            m_activeMenu->close(); // closing empty menu
            killTimer(m_myTimer);  // deactivating timer
            m_myTimer = 0;         // seting timer identifier to zero
            m_activeMenu = NULL;   // as well as active menu pointer to NULL
        }
    }
    

    It works great on every platform and does what I wanted. Hope, this would help someone. I've spent week trying to find this solution.