I want to know if QAction is clicked by left or right mouse button
Asked Answered
D

2

7

I have a QAction in QMenu. When QAction is triggered() I would like to know which button did it.

connect(YourAction, SIGNAL(triggered()), this, SLOT(actionclicked()));

void MainWindow::actionclicked(QMouseEvent *e)
{
    if (e->buttons() == Qt::RightButton) 
}

I can't do something like this because triggered() does not have such argument.

Dermoid answered 6/8, 2016 at 11:36 Comment(0)
C
4

As @mvidelgauz noticed, QAction is abstracted from input devices which may triggered the action. Nevertheless, if the action is used in your GUI, it has one or more associated widgets: tool buttons in a toolbar, entries in menu bar and so on. These widgets act like any other widgets, so they receive events which may be filtered with the use of installEventFilter and eventFilter. These two methods are inherited from QObject, so they are present in almost any Qt class. For example, let's create an application with QMainWindow and QAction called actionTest. Then let's turn the main window itself into an action filter for actionTest's associated widgets by overriding main window's eventFilter method:

bool eventFilter(QObject *obj, QEvent *ev) {
    //Catch only mouse press events.
    if(ev->type() == QEvent::MouseButtonPress) {
        // Cast general event to mouse event.
        QMouseEvent *mev = static_cast<QMouseEvent*>(ev);
        // Show which button was clicked.
        if(mev->button() == Qt::LeftButton) {
            qDebug() << "Left button!";
        }
        if(mev->button() == Qt::RightButton) {
            qDebug() << "Right button!";
        }
    }
    // In this example we just showed the clicked button. Pass the event
    // for further processing to make QAction slots work.
    return QMainWindow::eventFilter(obj, ev);
}

Then we need to install event filter object for all watched objects, which are widgets in our case. Let's do it in main window constructor:

MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    for(auto wgtPtr : ui->actionTest->associatedWidgets()) {
        wgtPtr->installEventFilter(this);
    }
}

Finally, add a slot for triggered() signal handling:

void on_actionTest_triggered() {
    qDebug() << "Action triggered!";
}

Now if you click the action menu entry with left mouse button, it will print

Left button!
Action triggered!

while for right mouse button the result will be

Right button!
Action triggered!

Note that widget event filtering is always performed before triggered() signal emission.

The above code is just an example, and MainWindow class is not the best place to host eventFilter method. In real code you may either:

  1. Create dedicated QObject subclass(es) for QAction widgets event filtering.
  2. Subclass QAction and override it's eventFilter method. In this case you may just save the result of QMouseEvent::button() in the QAction subclass object and later use it in triggered() signal handler. There is a minor inconvenience that Qt creator (at least up to v3.2.1) does not allow you to "promote" QActions in it's form designer, so you'll need to add actions to menus manually in window constructor.
  3. Subclass QMenu, QToolBar, etc.., and make them action filters? I don't know how can it be better than two former variants.

See also documentation about Qt event system.

Let's clarify case 2. Assume the class inherited from QAction is called MyAction. In order to make it work you need to install MyAction objects as filters for themselves (their widgets, to be more specific). You need to do it after widgets were created, so installing filter in MyAction constructor may be premature and lead to crashes. Better place for filter installation is a constructor of a class which owns MyAction object. Typically it's a widget or window class. So just add

for(auto wgtPtr : ui->myActionObject->associatedWidgets()) {
    wgtPtr->installEventFilter(ui->myActionObject);
}

to your window constructor after ui->setupUi(this) call. This code is like in the above example, but we use ui->myActionObject instead of this object as filter.

Commonplace answered 6/8, 2016 at 13:48 Comment(4)
Can I make eventFilter in class I use as QAction? I already have class which inherits from QAction, and I use it in my application. I overrided eventFilter method, but nothing is happening. Do I have to install it somewhere?Dermoid
@S.llous surely you can. I added clarification for case 2 from my answer which should make your code work. Try to print something to qDebug() from your eventFilter method to make sure it works properly.Commonplace
Will this work when I want to use it on QSystemTrayIcon? My application does not have GUI, only QActions in QSystemTrayIcon.Dermoid
@S.llous do those actions reside in a menu, which appears when you click tray icon? This should work.Commonplace
K
3

triggered() cannot have this argument by design because it by itself is not necessarily result of a mouse event:

This signal is emitted when an action is activated by the user; for example, when the user clicks a menu option, toolbar button, or presses an action's shortcut key combination, or when trigger() was called

You need to connect to mouse events if you need QMouseEvent as parameter. In fact Qt itself emits triggered() when (but not only as I highlighted in doc quote) framework receives mouse event from menu. So it looks like you'll need to do a similar thing in your code and add your own logic.

P.S. This discussion might be interesting for you

Kovno answered 6/8, 2016 at 12:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.