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.qml

    import 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 models

    Best 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


Log in to reply
 

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