QWidgetAction Not Being Returned After QMenu Close - c++

I have a QPushButton in a QWidgetAction in a QMenu. When the button is clicked, I want the action to trigger and the menu to close, returning which action was triggered. According to the docs, the widget itself must trigger the action directly.
Here's my code:
QMenu *menu = new QMenu();
QWidgetAction *widgetAction = new QWidgetAction(menu);
QPushButton *button = new QPushButton("Finish");
widgetAction->setDefaultWidget(button);
menu->addAction(widgetAction);
connect(button, SIGNAL(clicked()), widgetAction, SLOT(trigger()));
connect(widgetAction, SIGNAL(triggered()), menu, SLOT(close())); //Menu won't close without this
QAction* selectedAction = menu->exec(mapToGlobal(ui->pushButton->pos()));
if(selectedAction != NULL)
{
qDebug() << "no output from here";
}
However selectedAction always returns NULL. Regular QAction's added to the menu automatically close the menu and return pointers to themselves. Why doesn't QWidgetAction?
Thanks for your time!

Related

Adjust mainwindow size when sub widget is hidden

I'm writing a qt program and trying to resize mainwindow when sub widget is hidden, but there's some difference in Linux and Windows, and I don't know the property way to adjust size in both.
Here's my minimum code:
// MainWindow
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QWidget *mainWidget = new QWidget(this);
QVBoxLayout *mainlayout = new QVBoxLayout(mainWidget);
QPushButton *hide = new QPushButton(mainWidget);
subwidget *sub = new subwidget(mainWidget);
mainlayout->addWidget(hide);
mainlayout->addWidget(sub);
connect(hide, &QPushButton::clicked, sub, &subwidget::onHide);
setCentralWidget(mainWidget);
adjustSize();
connect(hide, &QPushButton::clicked, this, [&] {
QTimer::singleShot(0, this, [&] { adjustSize(); });
});
}
// Widget
subwidget::subwidget(QWidget *parent)
: QWidget{parent}
{
QVBoxLayout *vlayout = new QVBoxLayout(this);
QPushButton *btn1 = new QPushButton();
btn2 = new QPushButton();
vlayout->addWidget(btn1);
vlayout->addWidget(btn2);
}
void subwidget::onHide()
{
btn2->hide();
}
When I set the timer interval to 1, mainwindow will be the adjusted size when btn2 is hidden in Linux, but it doesn't work in Windows. How can I adjust the mainwindow in windows?
PS: I have tried to add a signal when subwidget is hidden and connect it to adjustSize like connect(sub, &subwidget::hidden, this, [&] { adjustSize(); });, but it also doesn't work.

QListWidget's editItem() outputs "edit: editing failed"

I am attempting to add an item to a QListWidget, select that item, and then begin editing the new item.
The item gets added, and it gets selected, but the line is not brought into a QLineEdit, or whatever it would attempt to use.
Here's the code for my widget, with the relevant code under the connect for "m_addButton":
Sequencer::Sequencer(QWidget* parent) :
QWidget(parent)
{
m_list = new QListWidget();
m_addButton = new QPushButton("Add Step");
m_removeButton = new QPushButton("Remove Step");
connect(m_addButton, &QPushButton::clicked, this, [&]()
{
m_list->addItem(""); // This part works,
m_list->setCurrentItem(m_list->item(m_list->count() - 1)); // This part works...
m_list->editItem(m_list->currentItem()); // This part puts out "edit: editing failed" into the console.
});
connect(m_removeButton, &QPushButton::clicked, this, [&]()
{
if (!m_list->selectedItems().isEmpty())
{
qDeleteAll(m_list->selectedItems());
}
});
QHBoxLayout* hLayout = new QHBoxLayout();
hLayout->addStretch();
hLayout->addWidget(m_addButton);
hLayout->addWidget(m_removeButton);
QVBoxLayout* vLayout = new QVBoxLayout();
vLayout->addWidget(m_list);
vLayout->addLayout(hLayout);
setLayout(vLayout);
}
By default a QListWidgetItem is not editable so if you want it to be, you must enable the Qt::ItemIsEditable flag:
connect(m_addButton, &QPushButton::clicked, this, [&]()
{
QListWidgetItem *item = new QListWidgetItem;
item->setFlags(item->flags() | Qt::ItemIsEditable); # enable
m_list->addItem(item);
m_list->setCurrentItem(item);
m_list->editItem(item);
});

QScintilla: How to add a user context menu to textEdit? (C++)

I'm struggeling with telling a QScitilla textEdit that is the main widget of my MainWindow app to accept showing a personalized context menu on right-clicking the mouse.
What works fine if I use a standard Qt5 textEdit fails if used with the QScintilla alternative. I tried it with defining a user menu from some actions:
void MainWindow::contextMenuEvent(QContextMenuEvent *event)
{
QMenu menu(this);
menu.addAction(cutAct);
menu.addAction(copyAct);
menu.addAction(pasteAct);
menu.exec(event->globalPos());
}
#endif // QT_NO_CONTEXTMENU
reacting on QContextMenuEvent, but the menu only shows up when I right-click an element of the MainWindow instead of the QScintilla textEdit. When I do within the textEdit, only the standard cut/copy/paste menu is shown.
How to implement that for QScintilla textEdit?
There are two methods:
Method 1: set Qt::CustomContextMenu for context menu policy of QScintilla text edit :
textEdit->setContextMenuPolicy( Qt::CustomContextMenu );
connect(textEdit, SIGNAL(customContextMenuRequested(const QPoint &)),
this, SLOT(ShowContextMenu(const QPoint &)));
}
void MainWindow::ShowContextMenu(const QPoint &pos)
{
QMenu contextMenu(tr("Context menu"), this);
QAction action1("Action 1", this);
connect(&action1, &QAction::triggered, this, []{
qDebug() << "On action 1 click !!!";
});
contextMenu.addAction(&action1);
contextMenu.exec(mapToGlobal(pos));
}
Method 2: Define a subclass of QScintilla then redefine the override function contextMenuEvent :
class MyQsciScintilla : public QsciScintilla
{
Q_OBJECT
public:
explicit MyQsciScintilla(QWidget *parent = nullptr);
void contextMenuEvent(QContextMenuEvent *event);
//....
};
void MyQsciScintilla::contextMenuEvent(QContextMenuEvent *event)
{
QMenu *menu = createStandardContextMenu();
menu->addAction(tr("My Menu Item"));
//...
menu->exec(event->globalPos());
delete menu;
}

How to convert QMenu to QMenuBar?

I have QMenu, which contains submenus:
QMenu menu;
// Add some submenus
menu.addMenu(new QMenu("1", menu));
menu.addMenu(new QMenu("2", menu));
menu.addMenu(new QMenu("3", menu));
I want to move these submenus from QMenu to QMenuBar:
QMenuBar* menubar = convertFromQMenu(&menu);
Here is how I think the implementation of convertFromQMenu might look like:
QMenuBar* convertFromQMenu(QMenu* menu) {
QMenuBar *menubar = new QMenuBar();
/*
for(QMenu* menu: menu->menus()) {
menu.setParent(menubar);
menubar.addMenu(menu);
}
*/
return menubar;
}
However, the commented code does not compile.
How to fix this?
You don't need that kind of 'conversion'. If you read carefully from the Qt official doc about QMenuBar and addMenu() member, you can easily add your QMenu to your QMenuBar:
QMenu menu;
// Add some menus
menu.add (new QMenu("1", menu));
menu.add (new QMenu("2", menu));
menu.add (new QMenu("3", menu));
QMenuBar menubar;
menubar.addMenu(&menu);
If you want to use the QMainWindow menu bar, the doc states :
In most main window style applications you would use the menuBar() function provided in QMainWindow, adding QMenus to the menu bar and adding QActions to the pop-up menus.
Example (from the Menus example):
fileMenu = menuBar()->addMenu(tr("&File"));
fileMenu->addAction(newAct);
Cause
for(QMenu* menu: menu->menus()) {
QMenu does not have a menus method.
Solution
The path to get to where you want is a little bit longer:
Use QWidget::actions to get a list of the actions, added to the menu
Get the QMenu associated with each action using QAction::menu
Now you can add the menu to the QMenuBar.
Note: Do not change the parenting of the menus and submenus.
Example
Based on your code, I would suggest you to implement convertFromQMenu like this:
QMenuBar *MainWindow::convertFromQMenu(QMenu *menu)
{
auto *menubar = new QMenuBar(this);
foreach (QAction *act, menu->actions())
if (act->menu())
menubar->addMenu(act->menu());
menu->deleteLater();
return menubar;
}
Here is how to test the suggested implementation:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
auto *menu = new QMenu("Menu", this);
// Add some menus
menu->addMenu(new QMenu("Menu 1", this));
menu->addMenu(new QMenu("Menu 2", this));
menu->addSeparator();
auto *submenu = new QMenu("Menu 3", this);
submenu->addActions(QList<QAction *>({new QAction("Action 1", this),
new QAction("Action 2", this),
new QAction("Action 3", this)}));
menu->addMenu(submenu);
setMenuBar(convertFromQMenu(menu));
setCentralWidget(new QWidget(this));
resize(300, 200);
}
I took the liberty to extend this example to switch between compact and extended form of the menus in the menu bar (not shown here). The full code is available on GitHub.
Result
As written, the given example produces the following result:

QTooltip for QActions in QMenu

I want to be able to show ToolTips for QMenu items (QActions). The best I have achieved is to connect the hovered signal of the QAction to a QTooltip show:
connect(action, &QAction::hovered, [=]{
QToolTip::showText(QCursor::pos(), text, this);
});
The problem is that sometimes the program will position the tooltip below the menu, specially when changing menus.
Is there any way to force the tooltip to show on top?
Since Qt 5.1, you can use QMenu's property toolTipsVisible, which is by default set to false.
See the related Qt suggestion.
You can subclass QMenu and reimplementing QMenu::event() to intercept the QEvent::ToolTip event and call QToolTip::showText to set the tooltip for the active action :
#include <QtGui>
class Menu : public QMenu
{
Q_OBJECT
public:
Menu(){}
bool event (QEvent * e)
{
const QHelpEvent *helpEvent = static_cast <QHelpEvent *>(e);
if (helpEvent->type() == QEvent::ToolTip && activeAction() != 0)
{
QToolTip::showText(helpEvent->globalPos(), activeAction()->toolTip());
} else
{
QToolTip::hideText();
}
return QMenu::event(e);
}
};
Now you can use your custom menu like :
Menu *menu = new Menu();
menu->setTitle("Test menu");
menuBar()->addMenu(menu);
QAction *action1 = menu->addAction("First");
action1->setToolTip("First action");
QAction *action2 = menu->addAction("Second");
action2->setToolTip("Second action");