Search code examples
qtqt5qwidget

Controlling a popup QWidget attached to a QToolButton


I want to get a Popup Widget to be shown when clicking on a QToolbutton.

This can be done by adding an action to the button itself. The popup will contain a three buttons (update, create and cancel) and a text input field.

I have some sample code with only one button I have shared as a Github repository.

The most relevant part of the code is:

    auto button = new QToolButton(this);
    button->setText(" AA ");

    auto popup = new Popup(button, this);
    auto popupAction = new QWidgetAction(this);
    popupAction->setDefaultWidget(popup);
    button->setPopupMode(QToolButton::InstantPopup);
    button->addAction(popupAction);

The result is as follow:

A working popup

I have two issues I cannot solve:

  • Getting the popup widget to right align to the button.
  • Getting the popup widget to close when one of the buttons inside of it are clicked.

Right align the popup

There is already a similar question: Set position (to right) of Qt QPushButton popup menu.

I can add the suggested code:

void Popup::showEvent(QShowEvent*)
{
    QPoint p = this->pos();
    QRect geo = clickedButton->geometry();

    this->move(p.x()+geo.width()-this->geometry().width(), p.y());
}

But only the content of the popup gets right aligned to the button, not the popup itself:

enter image description here

Closing the popup

If I click anywhere (but a widget) in the Popup it closes. I'm somehow fine with this.

But if I cannot manage to get a click on the button to close the popup.
I've tried to call the close() function but it only clears the content of the popup without closing it.

Can I get the button to trigger a signal and then close the popup?

I ask both questions as the same time, since they look very similar: both times it's the content and not the popup that is affected.


Solution

    • When you use a layout it has margins that prevent alignment.

    • The QMenu of the Popup can be accessed through kinship but this must be accessed when the button is pressed as this is created when it is first shown.

    popup.h

    #ifndef POPUP_H
    #define POPUP_H
    
    #include <QWidget>
    
    class QToolButton;
    
    class Popup : public QWidget
    {
        Q_OBJECT
    public:
        explicit Popup(QWidget* parent=nullptr);
    Q_SIGNALS:
        void clicked();
    };
    #endif
    

    popup.cpp

    #include "popup.h"
    
    #include<QWidget>
    #include<QVBoxLayout>
    #include<QPushButton>
    
    Popup::Popup(QWidget* parent)
        : QWidget(parent)
    {
        auto layout = new QVBoxLayout(this);
        layout->setContentsMargins(0, 0, 0, 0);
        layout->addStretch();
        auto updateButton = new QPushButton("Update");
        layout->addWidget(updateButton);
        connect(updateButton, &QPushButton::clicked, this, &Popup::clicked);
    }
    

    mainwindow.h

    
    MainWindow::MainWindow()
    {
        auto widget = new QWidget;
        setCentralWidget(widget);
        auto layout = new QHBoxLayout(widget);
        layout->addStretch();
    
        auto button = new QToolButton;
        button->setText(" AA ");
        layout->addWidget(button);
    
        auto popup = new Popup;
        auto popupAction = new QWidgetAction(this);
        popupAction->setDefaultWidget(popup);
        button->setPopupMode(QToolButton::InstantPopup);
        button->addAction(popupAction);
    
        connect(popup, &Popup::clicked, [popup](){
            if(QWidget *p = popup->parentWidget())
                p->close();
        });
    }