Load a portion of UI from a plugin QML (dynamic GUI) and expose C++ models to it



  • So, I'm trying to develop a plugin model for my app. Idea is simple:

    Have an interface that all plugins implement. I load/discover plugins at runtime.

    My host application would have dedicated areas in the UI, within which, these plugins would display their UI.

    The excellent "Plug & Paint tutorial":http://doc.qt.digia.com/4.6/tools-plugandpaint.html covers step (1) above pretty well. I have created an interface, developed an example plugin and the host is able to discover, load and invoke methods in them at runtime.

    For step (2), I have figured out how to have the plugin's UI (described via. QML) appear in the parents user interface (also described via. QML). I can use a QML Loader component in the main application QML, to load an external QML file into specific areas of the host application UI by setting the Loader.source at runtime. My main.qml looks like:

    @
    Rectangle {
    Item {
    width: card_1_1.width
    height: card_1_1.height
    Loader {
    id: loader
    source: <will be set dynamically>
    }
    }
    @

    However, I do not know how to make the plugin's C++ model available to the plugin's QML.

    In a normal non-plugin scenario, I would make my model available to QML using:

    @QQuickView.rootContext()->setContextProperty(...)@

    I do not know how to do this when the model I wish to expose will be discovered by the main app at runtime. My plugin interface looks like this:

    @
    class IMediaSource : public QObject
    {
    Q_OBJECT

    public:
    virtual ~IMediaSource() {}
    virtual QObject* getModel() = 0;
    virtual QString getQMLPath() = 0;
    };
    @

    I wish to expose the QObject* returned by IMediaSource::getModel() to the plugin's QML which is returned by IMediaSource::getQMLPath().

    My main app QML is loaded like so:

    @
    QGuiApplication app(argc, argv);
    QQuickView viewer;

    viewer.setSource(QUrl("qrc:///..."));
    viewer.showExpanded();
    return app.exec();
    @

    What I have tried:
    I've tried to use the following on the Loader elements' instance:

    @
    QQmlContext *context = mEngine->contextForObject(item); // item is an instance of Loader I'm interested in
    if (context != NULL)
    {
    context->setContextProperty("cardPlugin", this);
    context->setContextObject(this);
    }
    @

    Both methods return:

    @
    QQmlContext: Cannot set property on internal context.
    QQmlContext: Cannot set context object for internal context.
    @

    Workaround:
    One solution would be to iterate over all plugins and use:
    @
    QQuickView.rootContext()->setContextProperty("xxx", IMediaSource->getModel())
    @

    to pass on the models to the application QML's root context so they're available to all QML's loaded via. Loader.
    This apart from being clumsy also has the problem of name collisions. Two plugins could expose their models using the same name.

    I'm looking for a more elegant solution. Ideas are welcome.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.