gmanish
1st June 2014, 18:26
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 (http://qt-project.org/doc/qt-4.8/qml-loader.html) 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 this:
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.
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 (http://qt-project.org/doc/qt-4.8/qml-loader.html) 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 this:
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.