How to properly add a QMenu (and submenu) using a QAction - qt

I have been trying to add a submenu to a QMenu via QAction but it is not working.
As you can see below I am able to see the choices via right click on a QTableWidget.
However from the menu "Option" I should see the submenu, but I don't see it.
Below the procedure:
rostreewidget.h
class ROSTreeWidget : public QTreeWidget
{
Q_OBJECT
public:
ROSTreeWidget(QWidget *parent = nullptr);
signals:
void selectFrom();
void editLaserTable();
private:
QAction *mActionSELECT_FROM;
QAction *mActionEditLaserTable;
QAction *mAddMenu;
QActionGroup *actions1, *actions2;
QMenu *mMenu;
QMenu *submenu;
QAction *actionA_Setup;
};
rostreewidget.cpp
// Namespace ROSLaserItemTree
ROSTreeWidget::ROSTreeWidget(QWidget *parent) : QTreeWidget(parent)
{
mActionSELECT_FROM = new QAction(QIcon("/home/mapper/execute-command"), "SELECT * FROM", this);
mActionEditLaserTable = new QAction(QIcon(":"), "Edit LaserScan Table");
mAddMenu = new QAction(QIcon(":"), "Options", this);
addActions({ mActionSELECT_FROM,
mActionEditLaserTable,
mAddMenu});
connect(mActionSELECT_FROM, &QAction::triggered, [&]() {
emit selectFrom();
});
connect(mActionEditLaserTable, &QAction::triggered, [&]() {
emit editLaserTable();
});
connect(mAddMenu, &QAction::triggered, [&]() {
mMenu = new QMenu();
submenu = mMenu->addMenu( "A" );
QAction* actionA_Setup = submenu->addAction( "Setup" );
});
}
What I have done so far:
1) After following this source I was able to prepare the menu and the submenu to be inserted in the QAction but unfortunately it is not working.
As you can see from the print screen I am not able to see the choices under "Option".
2) Another thing I tried was going through this other source which was also clear, but still I could not figure out why the submenu is not being added in the QAction.
Thanks for pointing to the right direction for solving this issue.

Related

QMenu (and submenu) opens up in a different monitor location

I built an application that uses QMenu and its related submenu. The Qmenu is triggered to open via QAction.
The problem I have is that as soon as I call the QAction and I chose the QAction that carries the "Options", it should open the QMenu, but instead the QMenu open in another location of the application looking totally detached.
Below is the wrong behavior, where it is possible to see the QMenu opening in another location of the Desktop.
Here is the expected behavior where the QMenu opens up after selecting "Oprions":
I am not sure of why this is happening.
Below the snippet of code related:
rostreewidget.h
class ROSTreeWidget : public QTreeWidget
{
Q_OBJECT
public:
enum ROSType {
NULLPTR,
TABLE,
BASE,
MENU
};
ROSTreeWidget(QWidget *parent = nullptr);
ROSType rostype(ROSTreeItem *rosItem) const;
ROSType rostype() const {
return rostype(currentItem());
}
signals:
void selectFrom();
void editLaserTable();
private:
QAction *mActionSELECT_FROM;
QAction *mActionEditLaserTable;
QAction *mAddMenuOptions;
QMenu *submenuLaserScanA;
QAction *submenuActionA, *submenuActionB, *submenuActionC;
QMenu *submenuLaserScanB;
QAction *submenuActionAA, *submenuActionBB, *submenuActionCC;
QMenu *submenuLaserScanC;
QAction *submenuActionAAA, *submenuActionBBB, *submenuActionCCC;
};
rostreewidget.cpp
ROSTreeWidget::ROSTreeWidget(QWidget *parent) : QTreeWidget(parent)
{
mActionSELECT_FROM = new QAction(QIcon(":"), "SELECT * FROM", this);
mActionEditLaserTable = new QAction(QIcon(":"), "Edit LaserScan Table");
mAddMenuOptions = new QAction(QIcon(":"), "Options", this);
addActions({ mActionSELECT_FROM,
mActionEditLaserTable,
mAddMenuOptions});
connect(mActionSELECT_FROM, &QAction::triggered, [&]() {
emit selectFrom();
});
connect(mActionEditLaserTable, &QAction::triggered, [&]() {
emit editLaserTable();
});
connect(mAddMenuOptions, &QAction::triggered, [&]() {
mMenu = new QMenu();
submenuLaserScanA = mMenu->addMenu( "Menu A" );
mMenu->addSeparator();
submenuLaserScanB = mMenu->addMenu( "Menu B" );
mMenu->addSeparator();
submenuLaserScanC = mMenu->addMenu( "Menu C" );
submenuActionAAA = submenuLaserScanC->addAction("Setup C" );
submenuActionBBB = submenuLaserScanC->addAction( "Setup C" );
mMenu->show();
});
}
What I have done so far:
After following this source I was able to prepare the menu and the submenu to be inserted in the QAction and in fact all the related settings about it are as I need them to be.
I thought that linking the QAction inside the lambda function, exactly as I did for the other choices, would have worked, but instead what I am obtaining as soon as I launch the application is a QMenu that opens in another location of the Desktop without being linked to the main choice as shown below.
According to the documentation QMenu::addMenu parameters set are correct, so what is going on?
Thanks for pointing to the right direction for solving this issue.
Use this as a general pattern:
{
QMenu menu;
menu.addAction(...);
menu.addAction(...);
menu.exec(QCursor::pos()); // exec returns a triggered QAction
// process action
}
QMenu::exec() is modal. You don't need a QMenu instance outside the function.

How to add a QAction to a QListWidget

I have the following code:
roslaserscandoialog.h
public:
explicit ROSLaserScanDialog(QWidget *parent = nullptr);
~ROSLaserScanDialog();
QListWidgetItem *createItemFromAction(const QAction* action);
private slots:
void on_listWidget_itemClicked(QListWidgetItem *item);
private:
Ui::ROSLaserScanDialog *ui;
QAction *mAddMsgs;
QAction *mDeleteMsgs;
roslaserscandoialog.cpp
ROSLaserScanDialog::ROSLaserScanDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::ROSLaserScanDialog)
{
ui->setupUi(this);
connect(ui->listWidget,SIGNAL(on_listWidget_itemClicked(QListWidgetItem*)),this,SLOT(createItemFromAction(QListWidgetItem*)));
}
QListWidgetItem *ROSLaserScanDialog::createItemFromAction(const QAction *action)
{
Q_ASSERT( action );
QListWidgetItem *mAddMsgs = new QListWidgetItem();
mAddMsgs->setText( action->text() );
mAddMsgs->setToolTip( action->toolTip() );
mAddMsgs->setIcon( action->icon() );
// ...
return mAddMsgs;
}
void ROSLaserScanDialog::on_listWidget_itemClicked(QListWidgetItem *item)
{
mAddMsgs = new QAction(QIcon(":ros.png"), tr("Add New Message"), this);
mDeleteMsgs = new QAction(QIcon(":remove_item.png"), tr("Remove Message"), this);
}
What I have done so far:
I came across this post and also this one. Very useful as I set up my project in a very similar way, but nothing happens when I try to click on the QListWidget.
I know that in order to trigger the action I have to go to the slot called itemClicked as I did on the above code provided.
On the official documentation I was trying to apply what is advised but I don't know why nothing happens.
Please point to the right direction for solving this problem.
Look at console output, there should a warning about connect failing. If you look at your code, the reason should be pretty obvious. Consider
SLOT(createItemFromAction(QListWidgetItem*))
versus your method which isn't even a slot
QListWidgetItem *createItemFromAction(const QAction* action);
See the difference?
And then you have this slot:
void on_listWidget_itemClicked(QListWidgetItem *item);
which you are trying to use as a signal
SIGNAL(on_listWidget_itemClicked(QListWidgetItem*))
That obviously won't work.
It's a bit unclear what you want to happen when an item is clicked, but maybe you just should call createItemFromAction directly from the on_listWidget_itemClicked.
Additionally, add debug print or use breakpoint to verify that the on_listWidget_itemClicked is actually called when you click an item. If not, then you are missing connecting the relevant signal from your list view, ie. ui->setupUi(this); does not have that connect (in other words you did not do the connection in the GUI Designer).

Recursive repaint detected in popup window

I have a QPushButton that will open new window on clicked.
void showNewWindow()
{
PopupWindow *popup = new PopupWindow();
popup->show();
}
And PopupWindow is declared as following:
class PopupWindow : public QWidget {
Q_OBJECT
public:
PopupWindow(QWidget* parent);
void setContent(QString content) { this->content = content; }
QString getContent() { return content; }
private:
QString content;
private slots:
void handleContinue();
void handleRunToEnd();
};
Then I implement a its constructor:
PopupWindow::PopupWindow(QWidget *parent):QWidget(parent)
{
QHBoxLayout *hlayout = new QHBoxLayout();
QWidget *buttonWidget = new QWidget();
QPushButton *btnContinue = new QPushButton();
btnContinue->setText("Continue");
QPushButton *btnRunEnd = new QPushButton();
btnRunEnd->setText("Run till completion");
buttonWidget->setLayout(hlayout);
hlayout->addWidget(btnContinue);
hlayout->addWidget(btnRunEnd);
connect(btnContinue,SIGNAL(clicked()), this, SLOT(handleContinue()));
connect(btnRunEnd,SIGNAL(clicked()), this, SLOT(handleRunToEnd()));
QTextEdit *html = new QTextEdit();
html->setReadOnly(true);
html->setText("AAAA");
QVBoxLayout *layout = new QVBoxLayout();
this->setLayout(layout);
layout->addWidget(html);
layout->addWidget(buttonWidget);
}
My problem: whenever I click on the "Continue" or "Run till completion" buttons on Popup Window. The app crashed. I could see the error as following:
QApplication: Object event filter cannot be in a different thread.
QApplication: Object event filter cannot be in a different thread.
QApplication: Object event filter cannot be in a different thread.
QWidget::repaint: Recursive repaint detected
Please, help me to resolve this.

How to create a draggable (borderless and titleless) top level window in QT

I'd appreciate help creating a top-level window in Qt with the following characteristics. The window must be:
Borderless, titleless and lie on top of all other windows on the desktop (easy)
Draggable by clicking and dragging anywhere inside it (this what I need help with)
Constrained to the top border of the desktop while dragging (relatively easy)
Basically, I'm trying to collapse our QT application to a top-level icon on the top border of the desktop.
You'll find the answer to the first part in: Making a borderless window with for Qt, and the answer to the second part in Select & moving Qwidget in the screen.
Combining the two, and adding the last part is straightforward.
Here's how you could do it:
#include <QtGui>
class W: public QWidget
{
Q_OBJECT
Set up a borderless widget with a few buttons to lock/unlock and quit:
public:
W(QWidget *parent=0)
: QWidget(parent, Qt::FramelessWindowHint), locked(false)
{
QPushButton *lock = new QPushButton("Lock");
QPushButton *unlock = new QPushButton("Unlock");
QPushButton *quit = new QPushButton("&Quit");
connect(lock, SIGNAL(clicked()), this, SLOT(lock()));
connect(unlock, SIGNAL(clicked()), this, SLOT(unlock()));
connect(quit, SIGNAL(clicked()),
QApplication::instance(), SLOT(quit()));
QHBoxLayout *l = new QHBoxLayout;
l->addWidget(lock);
l->addWidget(unlock);
l->addWidget(quit);
setLayout(l);
}
public slots:
void lock() {
locked = true;
move(x(), 0); // move window to the top of the screen
}
void unlock() { locked = false; }
Do the mouse handling:
protected:
void mousePressEvent(QMouseEvent *evt)
{
oldPos = evt->globalPos();
}
void mouseMoveEvent(QMouseEvent *evt)
{
const QPoint delta = evt->globalPos() - oldPos;
if (locked)
// if locked, ignore delta on y axis, stay at the top
move(x()+delta.x(), y());
else
move(x()+delta.x(), y()+delta.y());
oldPos = evt->globalPos();
}
private:
bool locked;
QPoint oldPos;
};

how to add menu dynamically in Qt

I want to add, submenu to a menu item dynamically. How can I achive this?
I tried like this,
I have created an Action and submenu. Then I have added the submenu to action.
But, I have connected the “triggered” signal of action. its getting crash if I click on the action..
I have also handled the “aboutToShow” signal of menu, same its also getting crash when I click on action..
Here is the sampe code.
Submenu = new QMenu(this);
connect(Submenu, SIGNAL( aboutToShow()), this, SLOT(move ()));
QAction *test = new QAction(tr("Selection"), this);
test ->setMenu(Submenu);
menubar()->addAction(test);
I want to get the notification, before the display of submenu..
additonal information:
pleas try this code, in your main window constructor.
QAction *action = new QAction("Test",this);
QAction *dummyaction = new QAction("Testing",this);
QMenu *menu = new QMenu();
menu->addAction(dummyaction);
bool val= connect(menu, SIGNAL( aboutToShow()), this, SLOT( Move()));
val= connect(menu, SIGNAL( aboutToHide()), this, SLOT(Move()));
action->setMenu(menu);
this->menuBar()->addAction(action);
if I do like this, I am able to see one submenu item. But before that Move slot should call, its not getting called.. and even before hide also the same slot should call.. its not coming..
I tried the return values of connect.. its true only… so what is wrong with my code.. please say..
Such code should work:
QMainWindow wnd;
QAction *act = wnd.menuBar()->addMenu("SomeMenu")->addMenu("someSubmenu")->addAction("someAction");
QObject::connect(act,SIGNAL(triggered()),
someObj,SLOT(actionReaction()));
I think addMenu() addAction() should work in more reliable way. This approach works for me.
I'm not sure to understand exactly what you're willing to do into your Move() slot.
But here is your own code (I removed what seemed useless to me), modified so that it is not crashing on my computer :
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QAction>
#include <QMenu>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
private:
QMenu* menu;
QAction *dummyaction;
QMenu* m_pSubMenu;
private slots:
void Move();
};
#endif // MAINWINDOW_H
mainwindow.cpp :
#include "mainwindow.h"
#include <QMenuBar>
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
m_pSubMenu = NULL;
QMenuBar* pMenuBar = new QMenuBar(this);
setMenuBar(pMenuBar);
dummyaction = new QAction("Testing",this);
menu = new QMenu("Test", this);
menu->addAction(dummyaction);
this->menuBar()->addMenu(menu);
connect(menu, SIGNAL(aboutToShow()), this, SLOT(Move()));
}
void MainWindow::Move() {
if (!m_pSubMenu) {
m_pSubMenu = new QMenu(menu);
dummyaction->setMenu(m_pSubMenu);
}
QAction* pAction = new QAction("Test", this);
m_pSubMenu->addAction(pAction);
}
I don't know exactly what you want to do into your Move() slot, but as an example, each time the Move() slot is called, a new submenu item is added.
Hope this helps.

Resources