Set position (to right) of Qt QPushButton popup menu
Asked Answered
S

3

5

I am writing a popup menu for a Qt push button widget. Whenever the push button is clicked, a menu pops up (below the push button).

The popup menu is left-sided below by default.

Are there any ways to make the popup menu to pop up on the right side below the push button?

There is no set position function, so I wonder if there is some sophisticated way of doing it?

Here is some code (for popup menu):

QMenu *menuMode = new QMenu(this);
    min = menu ->addAction("In");
    mout = menu ->addAction("out");
ui->pushButtonMode->setMenu(menuMode);   //I am writing in MainWindow, that's there is ui
Skimp answered 28/7, 2015 at 6:14 Comment(0)
R
8

This can be done by subclassing QMenu and moving the popup menu where you want to have it in showEvent:

popupmenu.h

#ifndef POPUPMENU_H
#define POPUPMENU_H

#include <QMenu>

class QPushButton;
class QWidget;

class PopupMenu : public QMenu
{
    Q_OBJECT
public:
    explicit PopupMenu(QPushButton* button, QWidget* parent = 0);
    void showEvent(QShowEvent* event);
private:
    QPushButton* b;
};

#endif // POPUPMENU_H

popupmenu.cpp

#include "popupmenu.h"
#include <QPushButton>

PopupMenu::PopupMenu(QPushButton* button, QWidget* parent) : QMenu(parent), b(button)
{
}

void PopupMenu::showEvent(QShowEvent* event)
{
    QPoint p = this->pos();
    QRect geo = b->geometry();
    this->move(p.x()+geo.width()-this->geometry().width(), p.y());
}

mainwindow.cpp

...
PopupMenu* menu = new PopupMenu(ui->pushButton, this);
...
ui->pushButton->setMenu(menu);

It looks like this:

enter image description here

Remark answered 28/7, 2015 at 7:7 Comment(4)
Thanks, I am working on this ! May I ask why there is QPushButton* b in class PopupMenu ? And is it the showEvent function will be called every time I click the Push Button for the PopupMenu ?Skimp
Also, I came across these errors: popupmenu.obj : error LNK2001: unresolved external symbol "public: virtual struct QMetaObject const * __cdecl PopupMenu::metaObject(void)const " `popupmenu.obj : error LNK2001: unresolved external symbol "public: virtual void * __cdecl PopupMenu::qt_metacast(char const *)" popupmenu.obj : error LNK2001: unresolved external symbol "public: virtual int __cdecl PopupMenu::qt_metacall(enum QMetaObject::Call,int,void * *)" I came across these kinda errors before, but I also don't know why does it related to QMetaObjectSkimp
@Skimp QPushButton* b is used to tell the PopupMenu which button instance it should align to. yes, showEvent will be called every time the button is clicked. about your errors: have a look at this SO question and answersRemark
Thank you for the link!! I tried add the .h back to the cmakelist to generate moc will solve this LNK2001 (inspired by one of the ans.)Skimp
P
2

Another (imho) simpler approach would be:

void MainFrame::Slot_ShowMenu()
{
    auto pMenu = new QMenu(this);

    connect(pMenu, &QMenu::aboutToHide, pMenu, &QMenu::deleteLater);

    ...

    // Retrieve a valid width of the menu. (It's not the same as using "pMenu->width()"!)
    int menuWidth = pMenu->sizeHint().width();

    int x = mUI.myQPushButton->width() - menuWidth;
    int y = mUI.myQPushButton->height();

    QPoint pos(mUI.myQPushButton->mapToGlobal(QPoint(x, y)));

    pMenu->popup(pos);
}
Processional answered 24/2, 2018 at 15:54 Comment(0)
M
1

You should implement an eventFilter for your QMenu. In the eventFilter method, you need to calculate the position where your menu will be shown.

Here you have an example:

.pro

TEMPLATE = app

QT     += widgets
SOURCES += main.cpp \
           dialog.cpp

HEADERS += dialog.h

FORMS   += dialog.ui

main.cpp

#include <QtWidgets/QApplication>

#include "dialog.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Dialog dia;
    return dia.exec();
}

dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QtWidgets/QDialog>
#include <QMenu>
#include "ui_dialog.h"

class Dialog : public QDialog
{
    Q_OBJECT
public:
    Dialog();

protected:
    bool eventFilter(QObject * obj, QEvent *event);

private:
    QMenu *menu;
    Ui::Dialog m_ui;
};

#endif

dialog.cpp

#include "dialog.h"

Dialog::Dialog()
{
    m_ui.setupUi(this);

    menu = new QMenu("menu", this);
    menu->installEventFilter(this);
    QAction *action = new QAction("action#1", this);
    menu->addAction(action);
    m_ui.pushButton->setMenu(menu);
}

bool Dialog::eventFilter(QObject * obj, QEvent *event)
{
    if (event->type() == QEvent::Show && obj == m_ui.pushButton->menu())
    {
        int menu_x_pos = m_ui.pushButton->menu()->pos().x();
        int menu_width = m_ui.pushButton->menu()->size().width();
        int button_width = m_ui.pushButton->size().width();

        QPoint pos = QPoint(menu_x_pos - menu_width + button_width,
                            m_ui.pushButton->menu()->pos().y());

        m_ui.pushButton->menu()->move(pos);
        return true;
    }
    return false;
}
Microparasite answered 28/7, 2015 at 6:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.