2
votes

I have an app written in C++ (logics) and QML (UI). In C++ part I have a collection of the QML object (kind of event system)

This is the simplified code of C++ object:

class Config : public QObject
{
Q_OBJECT
    Q_ENUMS(DataEvent)
public:

    enum DataEvent {
        DataEventUndefined = 0,
        DataEventDateChanged
    };

    ~Config();
    Q_INVOKABLE void registerToEvent (DataEvent event, QQuickItem *item)
    {
        p_dataListeners.insert(event,item);
    }

private:
    QMap<DataEvent,QQuickItem *> p_dataListeners;
}

in QML object I call C++ function and it work like a charm. This is part of QML code:

Item {
    id: myQMLObject
    function init() {
        Config.registerToEvent(Config.DataEventDateChanged,myQMLObject);
    }
    function receiveEvent(eventType) {
    ...
    }
}

OK, now I want to call the QML function of one of saved QML objects:

    event = Config::DataEventDateChanged;
    QMapIterator<DataEvent,QQuickItem *> i(p_dataListeners);
    while (i.hasNext()) {
        i.next();
        if(event == i.key()) {
            QQuickItem *item = i.value();
            QMetaObject::invokeMethod(item, "receiveEvent",
                                      QGenericReturnArgument(),
                                      Q_ARG(Config::DataEvent, event));
        }
    }

But i get this error: QMetaObject::invokeMethod: No such method MyQMLObject_QMLTYPE_44::receiveEvent(Config::DataEvent)

What I do wrong?

1

1 Answers

4
votes

You must use QVariant as the argument type:

#include <QApplication>
#include <QtQuick>

class Thing : public QObject
{
    Q_OBJECT
    Q_ENUMS(DataEvent)
public:
    enum DataEvent {
        DataEventUndefined = 0,
        DataEventDateChanged
    };

    Thing() {}

public slots:
    void registerToEvent(QQuickItem *stuff) {
        DataEvent event = Thing::DataEventDateChanged;
        QMetaObject::invokeMethod(stuff,
            "receiveEvent",
            QGenericReturnArgument(),
//            Q_ARG(Thing::DataEvent, event));
            Q_ARG(QVariant, event));
    }
};

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QQmlApplicationEngine engine;
    Thing thing;
    engine.rootContext()->setContextProperty("thing", &thing);
    engine.load(QUrl(QStringLiteral("qrc:///main.qml")));

    return app.exec();
}

#include "main.moc"

main.qml:

import QtQuick 2.2
import QtQuick.Window 2.0

Window {
    visible: true
    width: 300
    height: 250

    Item {
        id: item

        Component.onCompleted: {
            thing.registerToEvent(item);
        }

        function receiveEvent(arg) {
            print(arg);
        }
    }
}

This is because the argument type for JavaScript functions in QML is QVariant. We can verify this with a small addition to qmetaobject.cpp:

diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp
index accefb1..e39539c 100644
--- a/src/corelib/kernel/qmetaobject.cpp
+++ b/src/corelib/kernel/qmetaobject.cpp
@@ -1455,6 +1455,10 @@ bool QMetaObject::invokeMethod(QObject *obj,
     }

     if (idx < 0 || idx >= meta->methodCount()) {
+        for (int i = 0; i < meta->methodCount(); ++i) {
+            QMetaMethod method = meta->method(i);
+            qDebug() << method.methodSignature();
+        }
         qWarning("QMetaObject::invokeMethod: No such method %s::%s",
                  meta->className(), sig.constData());
         return false;

When the method invocation fails, we iterate over every method of the object known to moc. With the example above, this outputs:

"destroyed(QObject*)"
"destroyed()"
"objectNameChanged(QString)"
"deleteLater()"
"_q_reregisterTimers(void*)"
"childrenRectChanged(QRectF)"
"baselineOffsetChanged(double)"
"stateChanged(QString)"
"focusChanged(bool)"
"activeFocusChanged(bool)"
"activeFocusOnTabChanged(bool)"
"parentChanged(QQuickItem*)"
"transformOriginChanged(TransformOrigin)"
"smoothChanged(bool)"
"antialiasingChanged(bool)"
"clipChanged(bool)"
"windowChanged(QQuickWindow*)"
"childrenChanged()"
"opacityChanged()"
"enabledChanged()"
"visibleChanged()"
"visibleChildrenChanged()"
"rotationChanged()"
"scaleChanged()"
"xChanged()"
"yChanged()"
"widthChanged()"
"heightChanged()"
"zChanged()"
"implicitWidthChanged()"
"implicitHeightChanged()"
"update()"
"_q_resourceObjectDeleted(QObject*)"
"grabToImage(QJSValue,QSize)"
"grabToImage(QJSValue)"
"contains(QPointF)"
"mapFromItem(QQmlV4Function*)"
"mapToItem(QQmlV4Function*)"
"forceActiveFocus()"
"forceActiveFocus(Qt::FocusReason)"
"nextItemInFocusChain(bool)"
"nextItemInFocusChain()"
"childAt(double,double)"
"receiveEvent(QVariant)"

Note the last item "receiveEvent(QVariant)".