0

Is it possible to create an ObjectModel from c++ at runtime?

I have a plugin based application, where every plugin create a QQmlComponent and setup the signal and slots then give the component to the main app for rendering in a ListView, for this end I want to have a ObjectModel in the c++ side and manipulate it there.

main.qml (main app interface):

import QtQuick 2.9
import QtQuick.Layouts 1.3
import QtQuick.Controls 2.2


ApplicationWindow {
    id: qmlMainWindow
    width: 1240
    height: 720
    minimumWidth: 270+600
    minimumHeight: 120+400
    visibility: "Maximized"
    visible: true
    title: "CTC - Tableau de bord"

    GridLayout {
        anchors.fill: parent
        columnSpacing: 0
        rowSpacing: 0
        columns: 2
        rows: 2
        HeaderArea {
            id: headerArea
            Layout.row: 0
            Layout.columnSpan: 2
            Layout.fillWidth: true
            Layout.fillHeight: true
            Layout.minimumHeight: 120
            Layout.maximumHeight: 120
        }
        NotificationArea {
            id: notificationArea
            Layout.row: 1
            Layout.column: 1
            Layout.fillHeight: true
            Layout.maximumWidth: 350
            Layout.preferredWidth: 300
            Layout.minimumWidth: 270
            model: notificationModel
        }
        MainArea {
            id: mainArea
            bgColor: "lightgray"
            Layout.row: 1
            Layout.column: 0
            Layout.fillWidth: true
            Layout.fillHeight: true
        }
    }

}

MainArea item:

import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import QtQml.Models 2.1

Item {
    objectName: "mainArea"

    function addReport(obj) {
        omodel.append(obj);
    }

    property alias bgColor: mainAreaBackground.color
    property ObjectModel omodel
    Rectangle {
        id: mainAreaBackground
        anchors.fill: parent
        color: "white"
        ListView {
            anchors.fill: parent
            model: omodel
        }
    }
}

At first attempt I wanted to access the MainArea item from c++ side and call the addReport function with QQuickItem* returned from a plugin, without luck.

main.cpp:

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QTimer>
#include <QtPlugin>
#include <QPluginLoader>
#include <QDebug>
#include <QtQmlModel>

#include "notificationmodel.h" // model used in the notification area
#include "interfaces/inotification.h" // interface for a plugin
#include "interfaces/ireport.h" // interface for a plugin (of interest for this post)

int main(int argc, char *argv[])
{
    QGuiApplication::setApplicationName("ctc_dashboard");
    QGuiApplication::setOrganizationName("CTC");
    QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QGuiApplication a(argc, argv);

    NotificationModel notificationModel(&a);

    QQmlApplicationEngine engine;

    QPluginLoader ploader; // I load the plugin which his task is to connect 
                           // to a QtRemoteObject on some server and create a QQuickItem 
                           // which will present some statistics.
    ploader.setFileName("plugins/affair_states/AffairStates.dll");

    engine.rootContext()->setContextProperty("notificationModel", &notificationModel);

    engine.load(QString("%1/%2")
                .arg(QGuiApplication::applicationDirPath())
                .arg("qml/main.qml"));
    if (engine.rootObjects().isEmpty())
        return -1;

    if(ploader.load()){
        IReports* plugin = qobject_cast<IReports*>(ploader.instance());
        if(plugin) {
            qDebug() << "Good plugin : " << plugin->name();
            QObject::connect(plugin, &IReports::notify, [&](NotificationModel::Notification n){
                notificationModel.addNotification(n);
            });
            QObject::connect(plugin, &IReports::newReport, [&](QQuickItem* i){
                qInfo() << "Signal recived";
                qDebug() << "New report " << i;
                qInfo() << engine.rootContext()->contextObject()->findChild<QObject*>("mainArea");
                qInfo() << "Omodel " << engine.rootContext()->contextProperty("omodel");
            });
        }
    }

    return a.exec();
}

IReport plugin interface :

#include <QtPlugin>

#include "inotification.h"
#include <QQuickItem>

class IReports: public INotification
{
    Q_OBJECT
public:
    IReports();
    virtual ~IReports();

    virtual QList<QQuickItem*> reports() = 0;
    virtual QString name() const = 0;
    virtual QString sectionName() const = 0;

signals:
    void newReport(QQuickItem* report);

};
#define IReports_iid "dz.ctc.dashboard.interfaces.IReports"
Q_DECLARE_INTERFACE(IReports, IReports_iid)

Main app screenshoot

Houss_gc
  • 727
  • 5
  • 27
  • So how is the ObjectModel defined? Where exactly does it come from? – dtech Aug 03 '17 at 13:01
  • it come from the [Qt library](http://doc.qt.io/qt-5/qml-qtqml-models-objectmodel.html) it's a standard model that render its objects. and in my case I want to render some qquickitems that are issued from the application plugins. – Houss_gc Aug 03 '17 at 13:07
  • I mean it is in the plugin right? Why are you using a regular library? There is the dedicated `QQmlExtensionPlugin` which as the name suggest is specially for QML. – dtech Aug 03 '17 at 13:09
  • It's not a qml plugin its a simple cpp plugin which construct a qml component create it and pass it to the main app fro rendering in the mainArea. the ObjectModel is a Qt qml Model not me who developed it, I am just using it to render the items issued from my cpp plugin. – Houss_gc Aug 03 '17 at 13:10
  • So you want to create a QML object that is not defined in QML (rather than just instantiating a QML object from C++), entirely from C++. – dtech Aug 03 '17 at 13:12
  • Yes sort of, what I want is to create the component hook up signal and slots and render it from cpp. just that the creation process is done at the app plugin level no the app level, it's sort of dynamic qml component where the does know nothing just that it's a QQuickItem that must be rendered in the MainArea listview – Houss_gc Aug 03 '17 at 13:13
  • What is this component, what does it contain? I mean it is easy to create QML stuff from C++ (and wrong), but you either need to specify a `.qml` file or provide a string containing qml code as a source. It doesn't happen from thin air, there is no public API to create "qml from C++" without any qml code. – dtech Aug 03 '17 at 13:15
  • Of course the component is a qml file. – Houss_gc Aug 03 '17 at 13:20
  • So what's the actual problem? The existing answer already links to the documentation on creating QML objects from C++? – dtech Aug 03 '17 at 13:20
  • the problem is how to create a ObjectModel QML type from c++, and if it possible to insert a Component created with one QQmlEngine to another engine. – Houss_gc Aug 03 '17 at 13:22
  • How many QML engines are you using? – dtech Aug 03 '17 at 13:24
  • for now two, one in the main app and one in the app plugin. but at the end it will be as many engines as there plugins. – Houss_gc Aug 03 '17 at 13:25
  • This is getting messy. So do you want to have multiple QML engines running simultaneously and use objects from one engine in another engine? Or do you want to have some plugin that defines some QML type you want to use in the base application? – dtech Aug 03 '17 at 13:30
  • The first one, i have no qml extension plugin here, the plugin that i have is connecting to a server getting some info presenting it in a qml compo which will be created with this plugin engine and rendered in the main app engine – Houss_gc Aug 03 '17 at 13:33
  • Well, tough luck, I don't think it is even possible. I don't really see why you need multiple engines to "connecting to a server getting some info" - you don't need any QML for this, you should fetch your info via a regular `QObject` and use that as a data source in your core application. To be honest, it doesn't sound that you have a clear idea what you are doing. – dtech Aug 03 '17 at 13:35
  • I don't recall any documentation on doing this, and IIRC objects are actually tightly associated with the engine they are created in, you cannot even change the context for an object, much less the engine. Once again, whatever it is that you want to do, there is definitely a better way that will also have the additional advantage of working ;) – dtech Aug 03 '17 at 13:37
  • Maybe you are right, i could creat a simple model fetch info and add items to model but I want the presented items to be more dynamic for example i can just fetch the qml code froma database creat the item on the fly and render it. – Houss_gc Aug 03 '17 at 13:38
  • Well, you don't need any plugins or C++ code for this, you can simply create a QML object from a text string directly from QML: https://stackoverflow.com/questions/16002310/dynamic-instantiation-of-qml-objects/16004056#16004056 – dtech Aug 03 '17 at 13:39
  • Heck you don't even need a server on the other side, you can directly load QML from http – dtech Aug 03 '17 at 13:42
  • What about signal handler ?? Can I hook it from the item that it's creating the component at runtime? – Houss_gc Aug 03 '17 at 13:43
  • I guess i need to explain the purpose of my application more clearly, sorry for this mess :p – Houss_gc Aug 03 '17 at 13:46
  • Yes of course, however if possible it is best to pre-implement them in the QML. In most cases you simply need access to the "core" object, which can be either done by using a singleton, a dynamic scope property, or even supplemented to the qml obejct that's being created in form of a parameter. And yes, it is always a good idea to explain what you want to do well when you ask others for help with doing it. – dtech Aug 03 '17 at 13:46
  • Ok thank you this is a good way to follow as parameter. – Houss_gc Aug 03 '17 at 13:48

1 Answers1

1

It is possible to create any QML object from C++, although it 99.99% of the cases it is bad practice that you shouldn't be doing, and an indication of wrong design that will most likely come back to bite you later on.

You should not create or manipulate QML objects from C++, what you should have is a well defined C++ interface that is exposed to QML so that QML objects can interact with it.

Whatever it is that you intend on doing, there is most likely a better way to do it. Show us some code so we can give you a more specific answer.

dtech
  • 47,916
  • 17
  • 112
  • 190