Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QAction::event: Ambiguous shortcut overload - 2 checkable actions for the same setting



  • Hi,

    I have an issue with the same shortcut on 2 QAction. They are linked to a boolean option of the application. The goal is to show both options at the same time to the user. The option can be toggled with a shortcut, but the shortcut cannot be the same, so it can only be added to one action, and is not visible on the other.

    auto coords_relative_action = new QAction("Relative coordinates", this);
    coords_relative_action->setShortcut(Qt::Key::Key_F6);
    coords_relative_action->setCheckable(true);
    coords_relative_action->setChecked(!isAbsolute());
    
    auto coords_absolute_action = new QAction("Absolute coordinates", this);
    //coords_absolute_action->setShortcut(Qt::Key::Key_F6); // Cannot do this
    coords_absolute_action->setCheckable(true);
    coords_absolute_action->setChecked(isAbsolute());
    
    auto coords_group = new QActionGroup(this);
    coords_group->addAction(coords_relative_action);
    coords_group->addAction(coords_absolute_action);
    

    Is there a way to use the same shortcut, or to link both actions so the shortcut is visible on both ?

    Or maybe there is a better way to show both label at the same time with a single button ?

    Regards, Tudal



  • Hi,

    There's some logical issue here.

    There are 2 options:

    1. you create 2 actions with 2 different shortcuts (ex F6 F7)
    2. you create 1 action with an unique shortcut (toggle behavior) , in this case, you have to change the text of the action according to the state ( relative or absolute)

    I'm using the toggle behavoir for example to show a tool window, if the tool is open, the menu text is "Hide tool" and "Show tool" if closed



  • Thank you. First solution is out because we already use many shortcuts... Also, it would be counter intuitive to enable and disable the option with a different shortcut I think. Second solution is doable, and in fact, we already us it for this exact setting, but I wanted the option to be more explicit and intuitive. It is not clear what the checked state means if the label changes. There are also other options for which this is not possible, so I need a more complete solution.

    I tried to ignore the shortcut in a custom QAction class and implement the real event with a QShortcut.

    class IgnoreShortcutAction : public QAction
    {
    public:
        using QAction::QAction;
    
    public: // QAction
        bool event(QEvent* event) override
        {
            if (event->type() == QEvent::Shortcut)
            {
                return false;
            }
    
            return QAction::event(event);
        }
    };
    

    But for some reason, Qt decides to dispatch the shortcut event alternating between each ambiguous action/shortcut. This means I do not receive the event half the time.

    If someone has a better idea, I would appreciate, but for now, I will simply use a QShortcut and manually enter the shortcut with QAction's text.



  • @Tudal said in QAction::event: Ambiguous shortcut overload - 2 checkable actions for the same setting:

    If someone has a better idea, I would appreciate, but for now, I will simply use a QShortcut and manually enter the shortcut with QAction's text

    I don't see any workaround, mainly because, in the first place, some OSes (ex MacOS) don't allow multiple menu items with the same shortcut ( seems logical)

    As an exercise, i try to use QWidgetAction (never done that before)
    The idea is to use 2 radio buttons in a horizontal layout:

    MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent)
    {
        // menu
        QMenuBar* menuBar= new QMenuBar;    
        QMenu* menu=menuBar->addMenu(tr("File"));	
        QAction* action=menu->addAction("Coordinates");
        action->setEnabled(false);
        QWidgetAction* wAction=new QWidgetAction(this);
        QWidget* w=new QWidget;
        QHBoxLayout* l= new QHBoxLayout;
        
        QRadioButton* r=new QRadioButton("Absolute");
        connect(r,&QRadioButton::clicked,this,&MainWindow::buttonStateChanged);
        l->addWidget(r);
        
        r=new QRadioButton("Relative      F6");
         r->setChecked(true);
        connect(r,&QRadioButton::clicked,this,&MainWindow::buttonStateChanged);
        l->addWidget(r);
        
        w->setLayout(l);
        wAction->setDefaultWidget(w);
        wAction->setShortcut(Qt::Key::Key_F6);
        menu->addAction(wAction);
        setMenuBar(menuBar);   
        
         connect(menuBar,&QMenuBar::triggered,this,&MainWindow::triggeredActions);
    }
    // slots
    void MainWindow::triggeredActions(QAction* action)
    {
        qDebug()<<action;
    }
    
    void MainWindow::buttonStateChanged(bool b)
    {
        QRadioButton* r=qobject_cast<QRadioButton*>(sender());
        if(r) qDebug()<<r->text()<<b;
    }
    

    Not very fond of the result and it's over complicated.



  • I see what you did. The WidgetAction acts as a container handling the shortcut. It is similar to a custom widget.

    As you said, it seems over complicated, so I will keep it simple for now, maybe by manually showing the shortcut in both actions, or in its parent.