A few months ago I wrote about passing extra arguments to slots in PyQt. Here, I want to briefly discuss how the same effect can be achieved with Qt itself.
C++ is not as dynamic as Python, so Python's approaches of using lambda or functools.partial won't work [1]. Fortunately, the Qt folks provided a solution that can make passing extra arguments to slots relatively simple. This is the QSignalMapper class.
I'll just show a partial code sample. Suppose we have two different QAction objects, and we want to connect both to the slot:
void onAction(const QString& what);
Further, we want each action to pass a different argument to onAction. Here's the relevant connections using QSignalMapper:
// #1
m_sigmapper = new QSignalMapper(this);
// #2
connect(m_action_file_new, SIGNAL(triggered()),
m_sigmapper, SLOT(map()));
connect(m_action_file_open, SIGNAL(triggered()),
m_sigmapper, SLOT(map()));
// #3
m_sigmapper->setMapping(m_action_file_new, "File->New");
m_sigmapper->setMapping(m_action_file_open, "File->Open");
// #4
connect(m_sigmapper, SIGNAL(mapped(QString)),
this, SLOT(onAction(const QString&)));
There are four distinct steps here:
- Create a new QSignalMapper object.
- Connect the triggered slots of the actions to the mapper's map slot.
- Let the mapper know via setMapping which action should pass which extra argument.
- Finally, connect the mapper's mapped(QString) signal to onAction.
This is it. Now, when m_action_file_new is triggered, onAction will be called with the argument "File->New", and so on.
Internally, QSignalMapper is quite simple. It contains a hash table mapping the sender QObject* to the argument (filled in setMapping). When its map slot is invoked, it looks up the sender in the hash and fires mapped with the appropriate argument.
There are some limitations to QSignalMapper - for example, you can't map arguments with arbitrary type [2], or map multiple arguments. The only mappings supported are:
void setMapping(QObject *sender, int id);
void setMapping(QObject *sender, const QString &text);
void setMapping(QObject *sender, QWidget *widget);
void setMapping(QObject *sender, QObject *object);
So if you need to pass some complex information to a slot, create a class derived from QObject and use that.
[1] | Well, C++ does have some high-level constructs like boost::bind and the new C++0x standard support for lambdas, but I don't believe either approach is formally supported by Qt's connect methods yet. |
[2] | Though I guess Qt could have implemented this with templates. |