Qt 5 assign slot with parameters to a QPushButton

Bankin picture Bankin · Jan 16, 2014 · Viewed 7k times · Source

I have a Qt application on C++ and I want to assign a slot to a QPushButton. But I want to pass some arguments because I have more than one QPushButton doing similar thing so I want one function but with a parameter in it but Qt keeps saying me that there is no slot like this. Can someone tell me why and how should I do it?

Thank you in advance

In the .h file I have: (it was private in the beginning but I changed it in searching of the problem)

public slots:
    void handleButton(int row, int col);

Then in the .cpp:

void fieldWindow::handleButton(int row, int col){
    cout << row << " " << col << endl;
}

And again in the same .cpp:

connect(this->buttonsField[i][j], SIGNAL(released()), this, SLOT(handleButton(i,j)));

This is done in two nested loop so i and j are well defined.

And my error is:

QObject::connect: No such slot fieldWindow::handleButton(i,j) in ..\Proj1\fieldwindow.cpp:41
QObject::connect:  (receiver name: 'fieldWindow')

I read something in the internet that I should say handleButton(int, int); but then how should I pass the arguments ?

Answer

leemes picture leemes · Jan 16, 2014

Sadly, a Qt signal-slot connection doesn't accept any arguments to be passed to the slot when the signal is called. This only works if the signal itself provides those arguments, but you can't add them in a connect statement.

But you're not the only one who wants to do this, so in Qt there is a class which does almost what you want: QSignalMapper. An instance of this class can be used as a "proxy" for a signal-slot connection: you connect (multiple) buttons to a slot of this class, and connect a signal of this class to your target slot. Then, for each "sender" instance (in your case buttons), you can tell the class which value to add to the called slot. Example:

QPushButton * button1 = ...;
QPushButton * button2 = ...;

QSignalMapper mapper;

connect(button1, SIGNAL(released()), &mapper, SLOT(map()));
mapper.setMapping(button1, 42); // Number to be passed in the slot

connect(button2, SIGNAL(released()), &mapper, SLOT(map()));
mapper.setMapping(button2, 1337); // Number to be passed in the slot

connect(&mapper, SIGNAL(mapped(int)), this, SLOT(handleButton(int)));

Sadly, as you can see this class can only handle a single parameter. For your case, you should choose a different method (I explained the above in case you encounter a similar problem again but with a single int parameter).

The alternative which fits your problem is to rely on sender() within the slot. Remember your mapping in a data structure like

QMap<QObject*,int> rows;
QMap<QObject*,int> cols;

and access those in your slot which takes no argument:

void fieldWindow::handleButton(){
    int row = rows[sender()];
    int col = cols[sender()];
    cout << row << " " << col << endl;
}

Of course, when initializing your UI, you need to put appropriate values in these maps. Or you could search your button in your existing array buttonsField.

Another method exists since Qt5 and C++11: Instead of a slot, you can handle the signals in a lambda function. This would look like this (the signal is named using a new syntax):

connect(this->buttonsField[i][j], &QPushButton::released, [=]{
    handleButton(i, j);
});

If you don't like the lambda syntax, it's also possible to use some helpers from the standard library to compose a functor instead of a lambda, or you can write your own functor class, but personally I find the lambda very readable.