2

In my application I have a QToolButton related to the presence of an USB Pen Drive. When the Pen drive is inserted I would like to show the QToolButton and create a context menu associated to the content of the pen drive.I have a different menu dynamically created to be assigned to the Button.

My code works well for the first time, but when I create a new menu it doesn't appear. In this last version of code, when I show the button for the second time I get the the previous menu (Dismount is the only item present) and when i click on the item it doesn't do anything.

EDIT: If I use the QAction instead of the QWidgetAction the code works fine. So it seems something related to the QWidgetAction of QLabel used inside of it.

The following is a simplified version of my code:

/* member variables */
QMenu *m_pqmConMenUSB;
QLabel m_MenuItem;

/* costructor */    
ui->tbDriveUSB->setContextMenuPolicy(Qt::CustomContextMenu);
m_pqmConMenUSB = NULL;
QObject::connect(ui->tbDriveUSB, SIGNAL(customContextMenuRequested(const QPoint&)),this, SLOT(showUSBCM(const QPoint&)));
m_MenuItem.setStyleSheet("QLabel { background-color : black; color : white; }");
m_MenuItem.setText("Dismount");
QFont fonte = m_MenuItem.font();
fonte.setPixelSize(16);
m_MenuItem.setFont(fonte);
QPalette ChePalette = m_MenuItem.palette();
m_MenuItem.setMinimumSize(0,32);
ChePalette.setColor(m_MenuItem.backgroundRole(), Qt::black);
ChePalette.setColor(m_MenuItem.foregroundRole(), Qt::white);
m_MenuItem.setPalette(ChePalette);

/*member functions*/
void  MainWindow::showUSBCM(const QPoint& pos)
{
    // copied from an example
    if (pos != QPoint(0,0)) {
        // Execute context menu
        if (m_pqmConMenUSB!=NULL) m_pqmConMenUSB->exec(pos);
    }
}

void MainWindow::OnUSBMounted()
{
    /* this static boolean is used to simulate a change in the menu content */
    static bool tryToChange = false;
    ui->tbDriveUSB->show();
    m_pqmConMenUSB = new QMenu(this);
    QWidgetAction *menuItemW = new QWidgetAction(this);
    menuItemW->setDefaultWidget(&m_MenuItem);
    menuItemW->setText("Dismount");
    connect(menuItemW,SIGNAL(triggered()), this, SLOT(DoDismount()));
    m_pqmConMenUSB->addAction(menuItemW);
    if (tryToChange)
    {
        menuItemW = new QWidgetAction(this);
        menuItemW->setDefaultWidget(&m_MenuItem);
        menuItemW->setText("Update");
        connect(menuItemW,SIGNAL(triggered()), this, SLOT(Update()));
        m_pqmConMenUSB->addAction(menuItemW);
    }
    tryToChange = !tryToChange;
    ui->tbDriveUSB->setMenu(m_pqmConMenUSB);
}

void MainWindow::OnUSBDismounted()
{
   ui->tbDriveUSB->hide();

   /* the first version of the code tries to destroy the menu with the following code, but it doesn't work
   /*ui->tbDriveUSB->setMenu(NULL);
   QAction *pAction;
   foreach (pAction, m_pqmConMenUSB->actions())
       pAction->disconnect(this);
       delete(m_pqmConMenUSB);
    m_pqmConMenUSB = NULL;*/

}

Rudy Barbieri
  • 389
  • 1
  • 4
  • 16

2 Answers2

3

A useful pattern to dynamically populate a menu associated with a QToolButton is to first create the menu and attach it to the button, but do not populate it. Then connect a slot to the QMenu::aboutToShow() signal. In the slot implementation, clear the contents of the menu and populate it as needed for the current state of the application.

Jason
  • 389
  • 2
  • 6
  • I will try to implement it in this way. Now i changed the code to always keep the menu created (i don't call delete of the QMenu), call clear of the menu and then attach the action, but the behaviour is the same. when I call the exec the menu contais the actions but it is unable to show. – Rudy Barbieri Oct 08 '13 at 15:43
  • By default, `QToolButton::popupMode` is set to `DelayedPopup` which means the menu is shown after holding the mouse down for a bit. Try changing this property to `InstantPopup`. Furthermore, you should not need to worry about the custom context menu policy or the showUSBCM() method as `QToolButton` will handle the display of the menu on its own. – Jason Oct 08 '13 at 18:29
  • Thank you, I need now the InstantPopup property – Rudy Barbieri Oct 09 '13 at 08:22
1

As I mentioned yesterday, the problem was related to QLabels. In my code I used two member variables of QLabel type. The QLabels weren't pointers. When I delete the action, the QLabels weren't able to show them again. I suppose it was related to the removeAction(d->menuAction); function which destroys QWidget associated to the QWidgetAction. That function was called when the ui->tbDriveUSB->setMenu(NULL); was called. I choose to use the QLabel just for stylesheet and size, but it's possibile to set that properties in the menu. This is enough for me. I think that, making a new QLabel when the QWidgetAction is created and delete it when the QWidgetAction is deleted, could make works the previous code. I haven't tested it.

In order to complete the answer, the following is my current code that works well

/* member variable */
QMenu *m_pqmConMenUSB;

/* constructor */
ui->tbDriveUSB->setContextMenuPolicy(Qt::CustomContextMenu);
QObject::connect(ui->tbDriveUSB, SIGNAL(customContextMenuRequested(const QPoint&)),this, SLOT(showUSBCM(const QPoint&)));
m_pqmConMenUSB = new QMenu(this);
QFont fonte = m_pqmConMenUSB->font();
fonte.setPixelSize(16);
m_pqmConMenUSB->setFont(fonte);
m_pqmConMenUSB->setStyleSheet("QMenu { background-color : black; color : white; }");
m_pqmConMenUSB->setMinimumSize(0,32);

/*member functions*/
void  MainWindow::showUSBCM(const QPoint& pos)
{
    if (pos != QPoint(0,0)) 
    {
         // Execute context menu
         if (m_pqmConMenUSB!=NULL) m_pqmConMenUSB->exec(pos);
    }
}

void MainWindow::OnUSBMounted()
{
    static bool tryToChange = true;
    ui->tbDriveUSB->show();
    QAction *menuItem = new QAction("Dismount",this);
    connect(menuItem,SIGNAL(triggered()), this, SLOT(DoDismount()));
    m_pqmConMenUSB->addAction(menuItem);
    if (tryToChange)
    {
        QAction *menuItem2 = new QAction("upDate",this);
        connect(menuItem2,SIGNAL(triggered()), this, SLOT(Update()));
        m_pqmConMenUSB->addAction(menuItem2);
    }
    tryToChange = !tryToChange;
    ui->tbDriveUSB->setMenu(m_pqmConMenUSB);
}

void MainWindow::OnUSBDismounted()
{
    printf("SEI UNO SMONTATO\n\r");
    ui->tbDriveUSB->setMenu(NULL);
    QAction *pAction;
    foreach (pAction, m_pqmConMenUSB->actions())
    {
        pAction->disconnect(this); // receiver
        delete pAction;
    }
    ui->tbDriveUSB->hide();
    m_pqmConMenUSB->clear();
}
Rudy Barbieri
  • 389
  • 1
  • 4
  • 16