Passing extra arguments to Qt slots

July 9th, 2011 at 10:45 am

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:

  1. Create a new QSignalMapper object.
  2. Connect the triggered slots of the actions to the mapper’s map slot.
  3. Let the mapper know via setMapping which action should pass which extra argument.
  4. 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.

http://eli.thegreenplace.net/wp-content/uploads/hline.jpg

[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.

Related posts:

  1. Passing extra arguments to PyQt slots
  2. Problem passing arguments to Python scripts on Windows
  3. New-style signal-slot connection mechanism in PyQt
  4. Python insight: beware of mutable default values for arguments
  5. Finding out where a function was called from

2 Responses to “Passing extra arguments to Qt slots”

  1. andyNo Gravatar Says:

    Very interesting.

    You seems to be very well prepared in Qt signal and slots.

    Is it possible to use slots in a WPF C# application? I mean when the main thread is a winform and a series of Qt object are running in a separate threads ?

    The problem I found is how to have the Qt event loop started for the main thread where the last parts of the application is. In other words how to obtain a dispatch of the event if the signal is cross thread and is sent as queued from the secondary thread to the main, because the main thread cannot process the events… (or do I have to call continuously the processevents() ?

    Thanks

  2. elibenNo Gravatar Says:

    andy,

    Unfortunately, I’m not familiar with WPF so I can’t help you here.

Leave a Reply

To post code with preserved formatting, enclose it in `backticks` (even multiple lines)