Connect QML signal to C++11 lambda slot (Qt 5)
-
wrote on 26 Mar 2013, 09:21 last edited by
Hello everyone,
New QtQuick user here and having a jolly nice time so far!
There is one question, however, for which I haven't managed to find the answer so far in the documentation. Imagine that you have a QML object that defines a signal like this:
@Rect {
id: bar
name: "bar"
signal foo();
property string baz;
}
@and you want to connect it to a slot that is not part of a QObject:
@auto root = qml_container.rootObject(); // returns the Rect defined above
root->connect(root, "foo()", = { std::cout << "foo!" << std::endl; });@Unfortunately, this fails because there is no connect() overload that takes a string signal and a function pointer slot. The new syntax can only take a function pointer signal and function pointer slot:
@root->connect(root, &SomeQObject::foo, = { /* whatever */ });@
But the QML signal is not defined as part of a concrete QObject known at compile-time (that's the reason of using QML in the first place!)
So what I would like to do is access the QML signal by querying the container object at runtime:
@auto prop = root->property("baz"); // works for properties
auto sig = root->???("foo"); // what's the correct API for this?@From what I can tell so far, if I could somehow get a pointer to the foo QML signal I should be able to connect it to a lambda slot. Does anyone have any ideas?
(The rationale behind this: clean separation between UI defined in QML and handlers defined in C++. I don't wish to define a QObject for every QML signal if I can somehow avoid it.)
Question originally asked here: http://stackoverflow.com/questions/15624800/connect-qml-signal-to-c11-lambda-slot-qt-5
-
Use the old syntax. That will kill the lambda, but I don't think there is another solution. You might try finding something in QMetaObject but I a quick look tells me it won't help you.
Or connect in JavaScript.
-
wrote on 27 Mar 2013, 00:18 last edited by
A signal is just a method. You can use the QMetaMethod accessors to find it. isSignal() should return true.
It's early in the morning and I haven't fully woken up yet, so I might be wrong.Cheers,
Chris. -
wrote on 27 Mar 2013, 09:37 last edited by
Indeed! I can use a QMetaMethod to access the signal at runtime and connect it to a QMetaMethod slot.
Unfortunately, it's not possible to get a QMetaMethod for a non-QObject slot (so no lambdas or free functions), which means I'm still stuck with the same problem: I cannot connect a QML signal to a lambda slot...
For the moment, I have developed an ugly workaround where I encapsulate the slot function into a SignalRouter class derived from QObject. This works, but is both in-elegant and a maintenance burden: for every different QML signal signature, I have to add a corresponding C++ signature to the SignalRouter class (huge "container class" smell). Templates might help here but, of course, MOC does not support templated QObjects so this is a no-go.
I'm starting to think that what I'm trying to do is simply impossible (at least in a clean way) given the current Qt 5 APIs. Does anyone know if Qt 5.1 adds any new functionality regarding signal-slot connections?
-
wrote on 28 Mar 2013, 00:05 last edited by
I really can't answer that, to be honest. Stephen Kelly might know, Thiago certainly would, so you could try asking on #qt on freenode.
Cheers,
Chris. -
wrote on 3 Nov 2020, 18:01 last edited by
Few years later, still no QMetaMethod::toSignalFunc(), poor onTheFly us !
But a solution exists though. You can create a unique slot to create all your QmlToLambda connection instead of roouting each signal trought a C++ declared slot.
Something like:
connect(root, qmlSig, this, QMetaMethod::fromSignalFunc(&Interface::myUniqueCppSlotForAllConnections);And the implementation for the slot would be something like:
void Interface::myUniqueCppSlotForAllConnections(QString) {
if(!properties().contains(name)){
(*properties()[name])(){
//whatever
} -
wrote on 3 Nov 2020, 18:26 last edited by fcarney 11 Mar 2020, 18:42
Use the Connections objectto connect to a signal on a known C++ object (setContextProperty or similar). Have that signal be connected to your lambda in C++.Edit:
You don't need the Connections object.class SignalHolder : public QObject { Q_OBJECT public: SignalHolder(){} signals: void relaySignal(QString msg); }; ... QQmlApplicationEngine engine; SignalHolder sh; sh.connect(&sh, &SignalHolder::relaySignal, [](QString msg){ qDebug() << "relay message:" << msg; }); auto context = engine.rootContext(); context->setContextProperty("signalholder", &sh);
In QML:
Item { id: qmlsignalholder signal relayMessage(string message) Component.onCompleted: { relayMessage.connect(signalholder.relaySignal) relayMessage("Hello Signal") } }