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.
Related
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.
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).
So what I am trying to do is when I press the showMenu (QAction), the container (QStackedWidget) changes the current widget to menuWidget AND when I press it again it hides.
Ok so I have managed to get this code:
connect(showMenu, SIGNAL(triggered()), map, SLOT(map()));
map->setMapping(menuWidget, container);
Object::connect(map, SIGNAL(mapped(QWidget *)), container, SLOT(setCurrentWidget(QWidget *)));
also if I run:
container->setCurrentWidget(menuWidget);
directly, it works fine, so I have not messed up in that way.
You should create a slot in your class where you show/hide menuWidget.
If you are using a checkable QAction object, then you can use QAction::toggled(bool checked) signal, and use the checked variable to determine if you should show or hide your widget.
If you're not using a checkable QAction object, then you should create a class member variable of type bool that you toggle in your slot:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
...
private:
bool toggleStatus; // set this to false in your constructor
...
};
void MainWindow::menuToggled()
{
toggleStatus = !toggleStatus;
if(toggleStatus)
{
container->setCurrentWidget(menuWidget);
}
else
{
container->setCurrentWidget(mdiContainer);
}
}
I have a problem with connecting custom menu in QListWidget, the connect function returns false. Here is the code:
I've got some main window class called MainWindow. In its constructor I have this line
connect(ui->notesWidget, SIGNAL(customContextMenuRequested(QPoint &)),
this, SLOT(contextMenuforNotesArea(QPoint &)));
where notesWidget is mentioned QListWidget.
ContextMenuforNotesArea(QPoint &) is defined like this
class MainWindow : public QMainWindow
{
public slots:
void contextMenuforNotesArea(QPoint &pos);
};
void MainWindow::contextMenuforNotesArea(const QPoint &pos){
QMessageBox::information(this, "a", "the function has been finally called");
QMenu contextMenu(tr("Context menu"), this);
contextMenu.addAction(new QAction(tr("Hello"), this));
contextMenu.exec(mapToGlobal(pos));
}
I have also changed the property contextMenu in the listWidget to customContextMenu through form designer.
Change your SLOT signature as follows in the header file
void contextMenuforNotesArea(const QPoint &pos);
Also make sure, when you are doing the connection in the constructor, you do it after the calling of setupUi
I'm adding a bunch of QActions to my main window's menus. These actions can also be triggered by the keyboard, and I want the shortcut to be visible in the menu, as usual, e.g.
-----------------
|Copy Ctrl+C|
-----------------
I can do this using QAction.setShortcut(). However, I don't want these QActions to be triggered by the shortcuts; I'm handling all keyboard input separately elsewhere.
Is this possible? Can I disable the shortcut in the QAction but still have the shortcut text (in this example Ctrl + C) in my menus?
EDIT: The way I ended up doing it is connecting to the menu's aboutToShow() and aboutToHide() events, and enabling/disabling the shortcuts so they are only active when the menu is shown. But I'd appreciate a cleaner solution...
You could inherit from QAction and override QAction::event(QEvent*):
class TriggerlessShortcutAction : public QAction
{
public:
...ctors...
protected:
virtual bool event(QEvent* e)
{
if (e->type() == QEvent::Shortcut)
return true;
else
return QAction::event(e);
}
};
This will cause any events of type QEvent::Shortcut sent to your actions to not trigger the 'triggered()' signals.
action.setText("Copy\tCtrl+C");
This will look like an action with a shortcut, but the shortcut is not actually installed.
Here is a full example:
#include <QtGui>
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
QMainWindow win;
QMenu *menu = win.menuBar()->addMenu("Test");
// This action will show Ctrl+T but will not trigger when Ctrl+T is typed.
QAction *testAction = new QAction("Test\tCtrl+T", &win);
app.connect(testAction, SIGNAL(triggered(bool)), SLOT(quit()));
menu->addAction(testAction);
// This action will show Ctrl+K and will trigger when Ctrl+K is typed.
QAction *quitAction = new QAction("Quit", &win);
quitAction->setShortcut(Qt::ControlModifier + Qt::Key_K);
app.connect(quitAction, SIGNAL(triggered(bool)), SLOT(quit()));
menu->addAction(quitAction);
win.show();
return app.exec();
}