How to set signals for each action in QMenu? - qt

for(auto s :listofPossibleValues){
// item =s;
action = myMenu.addAction("Set Value to "+s);
connect(action,SIGNAL(triggered(bool)),this,SLOT(menuClicked()));
}
void MainWindow::menuClicked(){
value = new QStandardItem(item);
model->setItem(mainindex->row(),mainindex->column(),value);
}
I add actions and connect signals to the slot to my menu using the code above. Previously I was using the item to be the text. But it will only work for last item.
Does anyone at least know how to get the action that I clicked on?
How can I make it work for each individual item rather than just the last one?

Use the triggered signal of QMenu:
connect(menu, SIGNAL(triggered(QAction*)), this, SLOT(menuClicked(QAction*)));
Then, in menuClicked():
void MainWindow::menuClicked(QAction *action) {
// do something with action
}

Related

How to find subsequent (all) SLOT from a subclass

I need to block specific buttons on an MMI.
I implemented a button blocking function in a subclass of QPushButton.
For this, I used the clicked() signal and blocked the button with blockSignals(true).
This means that with each button clicked on my MMI, 2 SLOTS are always called.
But when calling the blocking of a specific button, I get the first SLOT (clicked()) of my subclass, in which I block the button, then I then arrive in the original SLOT linked to this button, which is still called despite the blocking (the first time only).
How can I in my QPushButton subclass know the subsequent SLOTs linked to this button and avoid them (delete them)?
void QbtnStandardButton::slotButtonClicked(void)
{
if (modeProtection)
{
// Special mode to protect/unprotect the button
if (isProtected())
{
// Reset the protection
this->blockSignals(false);
}
else
{
// Set the protection: button will be unclickable
this->blockSignals(true);
}
modeProtection = false;
}
if (isProtected())
{
QMessageBox *pMsgBox = new QMessageBox(QMessageBox::Information,
"Protection",
"This button is protected!",
QMessageBox::Ok);
pMsgBox->exec();
pMsgBox->deleteLater();
// Here: remove subsequent SLOT of this button ?
}
}
I think it's very difficult if not impossible to find SLOTS linked to a button.
I worked around the problem by using an eventFilter() instead of a SIGNAL() in my base class.
In this case, I can filter the "clicked()" event before it is reissued.

Qt checkbox stateChanged event handler

In my Qt application, I want to use a checkbox to do A when it's toggled to unchecked, and do B when toggle to checked. The checkbox is hooked to foo(int).
connect(myCB, SIGNAL(stateChanged(int)), this, SLOT(foo(int)));
There's a problem when the sanity check fails (eg. some variable got invalid values), I want to just show error message and remain everything unchanged. So I toggle the checkbox again to revert it back to where it was. But it seems this action would trigger the callback function foo(int) again, which mess up everything. I don't want it to trigger the callback in this situation. How should I do? Or is there a better way? See the pseudo code below.
void foo(int checkState)
{
if (checkState == Qt::Unchecked) {
if (!passSanityCheck()) {
// show error message
checkBoxHandle->toggle();
return;
}
// do A when it's unchecked
}
else {
if (!passSanityCheck()) {
// show error message
checkBoxHandle->toggle();
return;
}
// do B when it's checked
}
return;
}
Connect QCheckBox::clicked(bool checked) signal to your slot:
QCheckBox *cb = new QCheckBox(this);
connect(cb, SIGNAL(clicked(bool)), this, SLOT(toggled(bool)));
This signal is not emitted if you call setDown(), setChecked() or toggle().

How can add action with QWidget.addAction()?

In Qt, I want to add some actions in a widget using QWidget.addAction().
I can easily do it with QToolBar.addAction(), but when I use QWidget.addAction(), it doesn't work.
How can I use QWidget.addAction()?
Here is my function:
void Reb::addActionToBar(QString *tabName, QAction *action)
{
//if tab exist, just add the action, else:
tab_widget->addTab(new QWidget(), *tabName);
for(int i = 0 ; i <= tab_widget->count() ; i++) {
if(tab_widget->tabText(i) == tabName) {
action.setParent(tab_widget->widget(i));
tab_widget->widget(i)->addAction(action);
}
}
}
And as you know tab_widget is a QTabWidget...
I have no error but i can't see my action in tab.
QWidget::addAction() does not do add the action to the UI - the only place where the widget's actions are shown is in the widget's context menu, given the right context menu policy.
QTabWidget has no means to display actions in its UI. Actions are usually displayed in toolbars or menubars, so you would need to add the action there.
As a side note, there is no need to pass QStrings by pointer, simply pass the QString by const reference:
void Reb::addActionToBar(const QString &tabName, QAction *action)
Also, your code has an off-by-one error, use i < tab_widget->count() instead of i <= tab_widget->count() to fix that.

How to iterate through a menu's actions in Qt?

I'm working in a project where I need to open (show or popup) automatically the items in the QMenuBar.
Let's say I have the next menu bar:
File Edit Help
-op1 -op1 -op1
-op2 -op2 -op2
To set an action (show the menu associated with that action) I use:
menuBar->setActiveAction(mymenuactionpointer);
As I know, I can use one of the following to get a list of pointers to the elements of QMenuBar:
QMenuBar::actions();
or
QList<Object*> lst1 = QMenuBar::findChildren<QObject*>();
QList<Object*> lst2 = QMenuBar::findChildren<QAction*>();
When I use QMenuBar::findChildren<QAction*>() or MenuBar::actions() I got a list of the menus in menubar, I mean, I got "File, Edit, Help" from my QMenuBar, the size of the QList in this case is 3.
When I use QMenuBar::findChildren<QObject*>() I got a list of QObject of size 6, which is the correct number of items in the menu bar. However, I have tried cast to QAction*
QAction *a = (QAction *)lst1.at(0);
QAction *a = qobject_cast<QAction*>(lst1.at(0));
QAction *a = dynamic_cast<QAction*>(lst1.at(0));
In all this cases a is not NULL, but when I try to get the action name QAction::title() it always causes me segmentation fault.
I have been searching and I found here that after getting the menubar actions list, one can ask to QAction::menu() (which returns a valid QMenu pointer if the item is a menu) to know if the item is a QMenu, if yes, one can repeat getting the actions list of that menu, and continue iterating. But this does not work for me, I expected that for
QList<Object*> lst2 = QMenuBar::findChildren<QAction*>();
each element "File, Edit Help" QAction::menu() returns a valid menu pointer, so I could get the list of the actions of each menu, but this does not work at all for me.
The correct way to enumerate a QMenu is to use the actions() functions, but there is a catch - some of the actions are submenus, and they need to be iterated recursively. In fact, each QMenu is associated with a QAction, and they both hold pointers to each other - see QMenu::menuAction() and QAction::menu().
It is crucial to understand that each QMenu is also associated with a QAction. So knowing that, the proper implementation is the following:
void enumerateMenu(QMenu *menu)
{
foreach (QAction *action, menu->actions()) {
if (action->isSeparator()) {
qDebug("this action is a separator");
} else if (action->menu()) {
qDebug("action: %s", qUtf8Printable(action->text()));
qDebug(">>> this action is associated with a submenu, iterating it recursively...");
enumerateMenu(action->menu());
qDebug("<<< finished iterating the submenu");
} else {
qDebug("action: %s", qUtf8Printable(action->text()));
}
}
}
Below is how to iterate through every menu item in the menu bar, it will also seek out any menus underneath so there is not need for recursive calling here.
// assuming you have a private QActionGroup* actions; defined in the header..
// ...and a slot named 'onAction(QAction*)' as well... this should work:
QList<QMenu*> lst;
lst = ui->menuBar->findChildren<QMenu*>();
actions = new QActionGroup(this);
foreach (QMenu* m, lst)
{
foreach (QAction* a, m->actions())
{
actions->addAction(a);
}
}
connect(actions,SIGNAL(triggered(QAction*)),this,SLOT(onAction(QAction*)));
As you can see, you can then connect a master slot to handle the various events an action might bring up (i just showed triggered here but you get the idea). Hope this helps.. someone..
Notes
I used the QActionGroup for example purposes on using the list you might iterate through, but you really shouldnt use that unless you are dealing with radio groups since thats what its for. Secondly, if you want the actions because you plan to link them into a single method to handle all items, i suggest you use QMenu's triggered/hovering signals or if you need to know when a menu is about to pop up, you'll need QMenuBar's aboutToShow() signal. I cant think of a reason (for me anyway) that you cant do what you need in those signals since you are passed the QAction* in the slot. But if you MUST do it the otherway, you can do it the way I showed above, you just might not want to use the QActionGroup because of radio grouping is what it is designed for. (you can work around that by not adding items that are 'checkable' into the group.
The reason the qobject_cast is failing is that there are only three QActions with the QMenuBar as the parent. The other three are different QObjects (my guess is the three QMenus), so the cast fails. The QActions associated with those menus are then under those, not the root QMenuBar. I fail to see why you can't build a master list of QActions by recursively iterating through the QMenus.
You may be able to just use the QAction pointer from your UI definition if you are after a known menu, that might not trigger the parent menus. If you are trying to automate testing, trigger() on your desired QAction is probably as detailed as you need. If you are trying to do things in response to user actions, modifying toolbars is probably a better means of contextual feedback, as it doesn't break focus. Some more details on what you're actually trying to accomplish would help.
this puts it all together:
template <class Function>
class QMenuBarIterator {
QMenuBar *i_barP;
void iterate_sub(Function f, size_t tabsL, QMenu* m) {
foreach (QAction *action, m->actions()) {
f(tabsL, action);
if (action->menu()) {
iterate_sub(f, tabsL + 1, action->menu());
}
}
}
public:
QMenuBarIterator(QMenuBar *barP) : i_barP(barP) {}
virtual void operator()(size_t levelL, QAction *actionP) {
}
void iterate(Function f) {
QList<QMenu *> menuBar = i_barP->findChildren<QMenu *>();
foreach (QMenu* m, menuBar) {
f(0, m->menuAction());
iterate_sub(f, 1, m);
}
}
};
/***************************************************************************/
class CMenuLogger {
public:
void operator()(size_t tabsL, QAction *action) {
SuperString tabStr(GetIndentString(tabsL)); // returns a string with tabsL tab characters in it
if (action->isSeparator()) {
qDebug("%s-------------------", tabStr.utf8Z());
} else {
qDebug("%s%s (%s)",
tabStr.utf8Z(),
qUtf8Printable(action->text()),
qUtf8Printable(action->objectName()));
}
}
};
then in your main:
{
QMenuBarIterator<CMenuLogger> bar(ui->menuBar);
bar.iterate(CMenuLogger());
}

Qt QGraphicsScene how to separate select and unSelect signals?

I am creating an app in qt, and i have come to a problem.
I have a qgraphics scene.
I create the scene and I have put some actions to take place when the user clicks
on a scene item.
I can detect the selectionChanged() signals, but:
The signal is emitted twice (once for the clicked item and once for the previously selected item (deselection), so the required actions take place twice for both items).
When an item is clicked, it remains selected and i can't click it again...
(i tried setting item->setSelected(false) but it gets in an infinite loop of selection/deselection...).
Anyone has any idea how to fix that?
What I am trying to achieve, is to have no action performed on deselection,
and to be able to re-click a clicked item and re-perform the action.
Define a slot yourSlot() and connect it to the signal selectionChanged(). In this slot you check if the item responsible for the signal emission is selected or not.
void yourSlot() {
QGraphicsItem *item = qobject_cast<QGraphicsItem *>(sender());
if (item) {
if ( item->isSelected() ) { //this item is selected
doSomethin();
//*deselect* the item so that it can be selected again
item->setSelected(false);
}
else { //the signal was fired because the item was deselected
//ignore()
}
}
}
I can't try it right now because I don't have Qt installed in this computer by I think it should work.

Resources