Как сделать QObject::connect к функции QJSValue

Рейтинг: 0Ответов: 1Опубликовано: 09.01.2023

Через qml можно легко определить функцию для слота создаваемого компонента MyComponent.connect(slotJsFunction); Как сделать это на плюсах? Существует ли какой нибудь способ сделать QObject::connect для QJSValue если это isCollable и QObject signal. Необходимо прямое соединение с js функцией, ведь как-то это делает qml.

Вот мой вариант функции, которая вызывает модальное окно, мне необходимо сделать автоматическое подхватывание функций которые я передаю в качестве параметра JS.

FileSelector.qml

navigator.popup("qrc:/tools/imageproc/UploadModal.qml", {
                      fileSelected: url => {
                        console.log(url)
                      }
                  });

navigator.cpp

void Navigator::popup(const QUrl &url, const QJSValue &data)
{
    QQmlApplicationEngine * engine = App::get()->engine().get();

    QWindow *mainWindow = qobject_cast<QWindow*>(engine->rootObjects().first());
    if(!mainWindow) return;

    QObject *mainLayout = mainWindow->findChild<QObject*>("mainLayout", Qt::FindDirectChildrenOnly);
    if(!mainLayout) return;

    QQmlComponent modalComponent(engine, url);

    QVariantMap cfg;
    cfg["parent"] = QVariant::fromValue(mainLayout); //REQURED PARENT!!!
    QObject *modal = modalComponent.createWithInitialProperties(cfg);

    //TODO connect to slots;
    QJSValueIterator it(data);

    const QMetaObject* metaObject = modal->metaObject();

    while (it.hasNext()) {
        it.next();
        QString name = it.name();
        if(it.value().isCallable()){
            for (int i = metaObject->methodOffset(); i < metaObject->methodCount(); ++i) {
                QMetaMethod metaMethod = metaObject->method(i);
                if (metaMethod.methodType() == QMetaMethod::Signal && metaMethod.name() == name){
                    // по идее ту должна вызываться функция fileSelected
                    //QObject::connect(modal, metaMethod, it.value(), name); // как сделать это???
                    break;
                }
            }

        }else{
            modal->setProperty(name.toLocal8Bit(), it.value().toVariant());
        }
    }

    //show modal
    QMetaObject::invokeMethod(modal, "open");
}

Ответы

▲ 0Принят

Решение простое:

  1. создаём временный объект
  2. передаём для invokeMethod в качестве параметра базовый объект, название функции и аргументы
  3. получаем автоматически соединённые функции из параметров и слоты объекта
  4. удаляем временный объект

P.S. решение очень удобно пробрасывать обработчики в стиле колбэков JS для QML

QQmlComponent connectorComponent(engine);
connectorComponent.setData("import QtQuick 2.15; Connections { function connect(parent, name, fn){ parent[name].connect(fn[name]); } }", QUrl());
    QObject * connector = connectorComponent.create();

    for(auto v = calls.begin() ; v != calls.end(); ++v){
        QMetaObject::invokeMethod(connector, "connect", Q_ARG(QVariant, QVariant::fromValue(object)), Q_ARG(QVariant, v.key()), Q_ARG(QVariant, QVariant::fromValue(data)));
}
delete connector;