Ryan Schaefer
Ryan Schaefer

Reputation: 55

Best way to selectively expose C++ core logic to QML

I would like to set a context property of a specific QML Component instead of in the root context. I do not want the property accessible outside of the component. Is there a way from C++ to access the context of the Component to only allow the named property to be accessible from within the component's context, and not from the global namespace? I would like to keep the QML declarative and to not create the component in C++ to access it's context.

//main.qml
Item {
    Button {
        // this should NOT work
        text: ctxProp.text
     }
     OtherQml {
     }
}

//OtherQml.qml
Item {
    Button {
        // this should work
        text: ctxProp.text
    }
}

//main.cpp
QGuiApplication app(art, argv);
QQmlQpplicationEngine engine;

// Some QObject Type
CtxProp ctxProp;
// I'd like to set the context such that only OtherQml.qml can access
// this context propery. Setting in root context property makes it global
engine.rootContext()->setContextProperty("ctxProp", &ctxProp);

Upvotes: 2

Views: 884

Answers (1)

dtech
dtech

Reputation: 49319

You should implement a singleton that will only be visible where it is imported. This is the best and most efficient solution for exposing core logic to QML, as there is no tree lookup involved, and it is only visible where you chose to import it.

qmlRegisterSingletonType<CtxProp>("Sys", 1, 0, "Core", getCoreFoo);
// and in QML
import Sys 1.0
...
doStuffWith(Core.text)

getCoreFoo is the name of a function that returns a CtxProp * value (any QObject * would do actually due to the usage of meta data). You may create it in the function, or just return a pointer to an existing instance. Some people claimed there might be issues if the function doesn't create it since it is presumably managed by the QML engine, however I've been using a pre-existing one that I am sharing across multiple QML engines and haven't had a problem with it, of course setting ownership to C++ explicitly, since QML cannot really be trusted with managing the lifetime of objects in more dynamic usage contexts.

// main.cpp

static Core * c;
static QObject * getCore(QQmlEngine *, QJSEngine *) { return c; }

int main(int argc, char *argv[]) {
  QGuiApplication app(argc, argv);
  QQmlApplicationEngine engine; 
  c = new Core; // create
  engine.setObjectOwnership(c, QQmlEngine::CppOwnership); // don't manage
  qmlRegisterSingletonType<Core>("Sys", 1, 0, "Core", getCore); // reg singleton
  engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); // load main qml
  return app.exec();
}

Upvotes: 7

Related Questions