3

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.

a.l.e
  • 792
  • 8
  • 16

2 Answers2

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

Your Popup widget is not the actual popup, but just a widget inside the real popup. So what you're moving is the widget inside the real popup and not the popup itself.

The solution in the question you linked to uses QMenu and it works with QMenu.

In your code replace

connect(button, &QToolButton::clicked, this, &MainWindow::showPopup);

auto updateButton = new QPushButton("Update");
auto popupAction = new QWidgetAction(this);
popupAction->setDefaultWidget(updateButton);
button->setPopupMode(QToolButton::InstantPopup);
button->addAction(popupAction);

with

button->setPopupMode(QToolButton::InstantPopup);
auto menu = new Popup(button, this);
auto action = new QAction("Test");
menu->addAction(action);
button->setMenu(menu);

Change your Popup class to extend/inherit QMenu, uncomment the showEvent method and remove everything from the constructor.

qmenu aligned to right side of button

EDIT

You can set a layout to the qmenu and add widgets to it.

auto menuLayout = new QGridLayout();
auto menuBtn1 = new QPushButton("Btn1");
auto menuBtn2 = new QPushButton("Btn2");
auto menuBtn3 = new QPushButton("Btn3");

menuLayout->addWidget(menuBtn1, 0, 0);
menuLayout->addWidget(menuBtn2, 0, 1);
menuLayout->addWidget(menuBtn3, 1, 0);

button->setPopupMode(QToolButton::InstantPopup);
auto menu = new Popup(button, this);
menu->setLayout(menuLayout);

qgridlayout inside qmenu

fbg13
  • 156
  • 1
  • 14
  • Thanks for the hints. I've tried to convert my `QWidget` in a `QMenu`, but it only works for the sample case, but not for the real one: in my real code I need to put a grid layout inside of the widget, which is not possible for a `QMenu`. – a.l.e Aug 23 '19 at 08:15
  • Updated the answer with example using qgridlayout. – fbg13 Aug 23 '19 at 08:33
  • Ok, thanks. I will go for eyllanesc solution for now (already started implementing it), but I'm sure that other people will appreciate your solution with the layouts. If I have the time I will add both solution as branches to the Github repository. – a.l.e Aug 23 '19 at 09:43
2
  • 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();
    });
}
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • I did try to access the parent before, but failed. Probably, seeing posted it by you, made me try with more faith in it and, indeed, using `parentWidget()` works for both moving the widget (from inside `showEvent()`) and for closing it (in my `doUpdate()` slot triggered when the button is clicked). Thanks! – a.l.e Aug 23 '19 at 08:22
  • @a.l.e I have created a PR for your repo so you can quickly test it. – eyllanesc Aug 23 '19 at 08:27