Solved Exposing C++ backend to QML
-
Hi
I can see two methods: qmlRegisterType and setContextProperty
Is it possible to register backend.h in main.cpp which then will be exposed in main.qmlimport io.qt.examples.backend 1.0 BackEnd { id: backend }
And then if I create "QList<QObject*> dataList" of some sort in backend.cpp or its child classes I can use this as a model for example for Repeater somewhere in qml files eg. like this:
Repeater { model: backend.someClass.dataList
The idea is to have single "entry point" from QML to C++ classes
Will this work?
Best Regards
Marek -
Well this will not work.
Digging deeper, I would like some clarify this:
I have created subclassed QAbstractListModel based on this Sailfish nice example
If I register this model:qmlRegisterType<DemoModel>("com.example", 1, 0, "DemoModel");
I can't pass any arguments during registration - according to this: QML needs to be able to instantiate the type, calling it with only parent QObject.
So if my model needs to get the data from C++ part of the application, eg. from xmlMgr that reads files from directory, it can't be exposed to QML with qmlRegisterType - am I get this right?In case model needs to get data from C++ part I need to use
demoModel=new DemoModel(xmlMgr); demoModel->init(); view.rootContext()->setContextProperty("demoModel", demoModel);
Is this how it supposed to be, or am I missing something?
Best Regards
Marek -
@Marek If you have one object which you have created in C++ you can expose that object to be used in QML with setContextProperty. If a model is based on QAbstractItemModel and can be used automatically in C++ views it should work automatically with QML views, too. So I think your last comment is correct - you need only setContextProperty for it to work.
-
@Eeli-K Thanks for answer.
I hoped someone will add something about qmlRegisterType used with models (first approach), because as I see it now... it pretty much useless for registering modelsBest Regards
Marek -
Hi @Marek,
you don't need to create any instances of DemoModel in your C++. qmlRegisterType is enough, but you also need somehow to trigger update of your model. In my case I've created Q_INVOKABLE void update(); method in my model's class implementation which is being called each time data in model should be refreshed.
Of course don't forget to override:
int rowCount(const QModelIndex &parent);
QVariant data(const QModelIndex &index, int role);
methods in your model implementation.
Here you have great description of model-view-delegate concept: http://doc.qt.io/qt-5/model-view-programming.html -
Hi @osmial
I don't create DemoModel instance in C++. I know it will be created by QML and I know about Q_INVOKABLE.
Lets say you have some cpp class dataMgr - it reads data from external source like tcp socket.
This Data is needed for DemoModel, and you have DemoModel defined in cpp.
How do you call DemoModel Q_INVOKABLE void update() from QML if DemoMode does not know about dataMgr ?
DemoModel is instantiated in QML without pointer to dataMgr and you cant access DataModel from cpp.I assume that your update() from DemoModel reads data from some source but this process is completely isolated from the rest of cpp part of application, right?
Best Regards
Marek -
I have ended up with such a solution:
int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication app(argc, argv); QQmlApplicationEngine engine; BackEnd backend(&engine); engine.load(QUrl(QLatin1String("qrc:/main.qml"))); if (engine.rootObjects().isEmpty()) return -1; backend.init(); return app.exec(); }
Backend contructor is before engine load so the models are defined in QML
Backend init is after engine load to get rootObject, signals etc.Backend - C++ part of the application
#include "backend.h" BackEnd::BackEnd(QQmlApplicationEngine *engine) { this->engine=engine; xmlMgr=new XmlMgr(this); itemModel=new ItemModel(this); xmlMgr->init(); itemModel->loadData(xmlMgr); context = engine->rootContext(); context->setContextProperty("ItemModel",itemModel); } void BackEnd::init() { rootObject=engine->rootObjects().first(); connect(rootObject,SIGNAL(itemClicked(int)),this,SLOT(itemClicked(int))); } void BackEnd::itemClicked(int item_id) { qDebug()<<"BackEnd::itemClicked item_id:"<<item_id; int catalog_id=itemModel->getCatalogId(item_id); if(catalog_id) itemModel->setSelectedCatalog(catalog_id); }
QML part
ApplicationWindow { visible: true width: 640 height: 480 title: qsTr("Hello World") signal itemClicked(int value) header: Header { height: 60; width: parent.width } SwipeView { id: swipeView anchors.fill: parent Page1 { } Page { MainItem2 { id: mainItem visible: true } } } }
This works well for me.
Best Regards
Marek