Passing an argument to a slot
Asked Answered
Z

6

98

I want to override mouseReleaseEvent with a bunch of QActions and QMenus...

connect(action1, SIGNAL(triggered()), this, SLOT(onStepIncreased()));

connect(action5, SIGNAL(triggered()), this, SLOT(onStepIncreased()));

connect(action10, SIGNAL(triggered()), this, SLOT(onStepIncreased()));

connect(action25, SIGNAL(triggered()), this, SLOT(onStepIncreased()));

connect(action50, SIGNAL(triggered()), this, SLOT(onStepIncreased()));

So I want to pass an argument to the slot onStepIncreased (as you can imagine they are 1,5,10,25,50). Do you know how I can do it?

Zeke answered 1/3, 2011 at 10:20 Comment(1)
Instead of passing parameters, consider analyzing sender() inside the signal.Jessi
Z
130

Use QSignalMapper. Like this:

QSignalMapper* signalMapper = new QSignalMapper (this) ;
connect (action1, SIGNAL(triggered()), signalMapper, SLOT(map())) ;
connect (action5, SIGNAL(triggered()), signalMapper, SLOT(map())) ;
connect (action10, SIGNAL(triggered()), signalMapper, SLOT(map())) ;
connect (action25, SIGNAL(triggered()), signalMapper, SLOT(map())) ;
connect (action50, SIGNAL(triggered()), signalMapper, SLOT(map())) ;

signalMapper -> setMapping (action1, 1) ;
signalMapper -> setMapping (action5, 5) ;
signalMapper -> setMapping (action10, 10) ;
signalMapper -> setMapping (action25, 25) ;
signalMapper -> setMapping (action50, 50) ;

connect (signalMapper, SIGNAL(mapped(int)), this, SLOT(onStepIncreased(int))) ;
Zoogloea answered 1/3, 2011 at 10:57 Comment(4)
I still remember times, when Qt didn't have QSignalMapper, and the only solution was setting property on objects connected to same slot and using sender()->property(...)Frigorific
@Kamil Klimek You didn't have to; you could have written your own mapper :)Speculator
How to use this if my context parameter targets a to a class which has no acess to the actions? so eitherway, the context signalmapper wouldn't have access to the actions, or if I'd have the signalmapper into the same class, would be the wrong context for the conecting slots.Mondragon
Worth noting (2018, Qt5, C++11) that QSignalMapper is deprecated. From the link: "This class is obsolete. It is provided to keep old source code working. We strongly advise against using it in new code." The answer by @kuba, below, is now a better one.Digestion
E
151

With Qt 5 and a C++11 compiler, the idiomatic way to do such things is to give a functor to connect:

connect(action1,  &QAction::triggered, this, [this]{ onStepIncreased(1); });
connect(action5,  &QAction::triggered, this, [this]{ onStepIncreased(5); });
connect(action10, &QAction::triggered, this, [this]{ onStepIncreased(10); });
connect(action25, &QAction::triggered, this, [this]{ onStepIncreased(25); });
connect(action50, &QAction::triggered, this, [this]{ onStepIncreased(50); });

The third argument to connect is nominally optional. It is used to set up the thread context in which the functor will execute. It is always necessary when the functor uses a QObject instance. If the functor uses multiple QObject instances, they should have some common parent that manages their lifetime and the functor should refer to that parent, or it should be ensured that the objects will outlive the functor.

On Windows, this works in MSVC2012 & newer.

Erlandson answered 14/3, 2014 at 16:57 Comment(3)
This combination of C++11 lambdas and Qt 5's ability to connect a functor to a signal is such a useful and yet underrated feature.Adhere
I adapted this solution to my case. However, it throws an error if I use SIGNAL(triggered(bool)) instead of &QAction::triggered. Can somebody please explain to me why?Clite
It doesn't "throw" an error. The compiler complains, and the error message should tell you why: there's no QObject::connect overload that takes a const char * as the 2nd argument and a functor as the third or fourth argument. The Qt4-style connect syntax doesn't mix with the new syntax. If you wish to use the old syntax, you forfeit the ease of connecting to functors (even though it could be approximated if you had a C++11 compiler but used Qt 4).Lefton
Z
130

Use QSignalMapper. Like this:

QSignalMapper* signalMapper = new QSignalMapper (this) ;
connect (action1, SIGNAL(triggered()), signalMapper, SLOT(map())) ;
connect (action5, SIGNAL(triggered()), signalMapper, SLOT(map())) ;
connect (action10, SIGNAL(triggered()), signalMapper, SLOT(map())) ;
connect (action25, SIGNAL(triggered()), signalMapper, SLOT(map())) ;
connect (action50, SIGNAL(triggered()), signalMapper, SLOT(map())) ;

signalMapper -> setMapping (action1, 1) ;
signalMapper -> setMapping (action5, 5) ;
signalMapper -> setMapping (action10, 10) ;
signalMapper -> setMapping (action25, 25) ;
signalMapper -> setMapping (action50, 50) ;

connect (signalMapper, SIGNAL(mapped(int)), this, SLOT(onStepIncreased(int))) ;
Zoogloea answered 1/3, 2011 at 10:57 Comment(4)
I still remember times, when Qt didn't have QSignalMapper, and the only solution was setting property on objects connected to same slot and using sender()->property(...)Frigorific
@Kamil Klimek You didn't have to; you could have written your own mapper :)Speculator
How to use this if my context parameter targets a to a class which has no acess to the actions? so eitherway, the context signalmapper wouldn't have access to the actions, or if I'd have the signalmapper into the same class, would be the wrong context for the conecting slots.Mondragon
Worth noting (2018, Qt5, C++11) that QSignalMapper is deprecated. From the link: "This class is obsolete. It is provided to keep old source code working. We strongly advise against using it in new code." The answer by @kuba, below, is now a better one.Digestion
D
14

The QObject::sender() function returns a pointer to the object that has signaled to the slot. You could use this to find out which action was triggered

Diapophysis answered 1/3, 2011 at 10:23 Comment(1)
I beg your pardon? The slot is a member of a subclass of QObject, thus it has also a QObject::sender() member. Just call sender(), and you will be given a QObject* pointing to your action. After that you can use objectName() or property() of an acquired action to collect more information. You can also convert it to an action object if you really want to, but I wouldn't recommend that.Physiography
H
4

You can use std::bind This is a functional object adapter that allows functional objects to be adaptee to a given number of parameters.

For example, you create your own chat server. That contains two classes: ChatServer and ServerWorker.

ChatServer is QTcpServer class and ServerWorker is QTcpSocket ( manage the socket on the server side).

Signals in ServerWorker header:

void error();

In your ChatServer header you define these private slots:

void userError(ServerWorker *sender);

In cpp file you create these object and in incomingConnection method that run after socket connect, you connect slots and signals using std::bind:

void ChatServer::incomingConnection(qintptr socketDescriptor)
{
    //some code
    connect(worker, &ServerWorker::error, this, std::bind(&ChatServer::userError, this, worker));
}

std::bind creates a functor with some fixed arguments. For example connect(worker, &ServerWorker::error, this, std::bind(&ChatServer::userError, this, worker)); will result in this->userError(worker); to be called every time the worker emits the error signal.

userErrorslot is executed every time a socket connected to a client encounters an error. It has signature:

void ChatServer::userError(ServerWorker *sender)
{
    //some code
}

Example

Heaume answered 21/1, 2021 at 13:15 Comment(0)
A
1

Maybe you can subclass QAction with an m_increase member variable.
Connect the triggered() signal to a slot on your new QAction subclass and emit a new signal (e.g. triggered(int number)) with the correct parameter.
e.g.

class MyAction:public QAction  
{  
public:  
    MyAction(int increase, ...)  
        :QAction(...), m_increase(increase)
    {  
        connect(this, SIGNAL(triggered()), this, SLOT(onTriggered()));  
    }  
protected Q_SLOTS:  
    void onTriggered()  
    {  
        emit triggered(m_increase);  
    }  

Q_SIGNALS:
    void triggered(int increase);   

private:  
    int m_increase;  
};
Arteaga answered 1/3, 2011 at 11:0 Comment(0)
S
0
QVector<QAction*> W(100);
 W[1]= action1;
 W[5]= action5;
 W[10]= action10;
 W[25]= action25;
 W[50]= action50;

for (int i=0; i<100; ++i)
{
  QSignalMapper* signalmapper = new QSignalMapper();
  connect (W[i], SIGNAL(triggered()), signalmapper, SLOT(map())) ;
  signalmapper ->setMapping (W[i], i);
  connect (signalmapper , SIGNAL(mapped(int)), this, SLOT(onStepIncreased(int)));
} 
Sn answered 23/2, 2018 at 9:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.