QML: C++ classes with "bring your own component"

Viewed 211

I'm trying to develop a Qt C++ application, with a QML frontend, but I hit a roadblock.

This is what I have so far:

  • A Factory class that outputs a choice of objects. These objects, that I'm going to call "controllers", control different pieces of hardware.
    • The Factory would be exposed to the QML layer with setContextProperty.
    • The controller would be chosen basically with a combo box controlling the factory.

Now, for the tricky bit. I want that the "controllers" behave in a "bring your own component" way. This means that they would have a method returning the respective QML file for their controller. That shouldn't be to hard to do, it's basically biding a Loader to a method of the Factory/Manager saying the file with the component to load into a placeholder.

But the problem is: how can this newly created component and this newly created controller know and talk to each other? This is something I did before with QWidgets, just having pointers between the classes. Quite trivial.

I tried an architecture like this before for QWidgets, but seems to not be ideal for QML.

I made this drawing of what I would ultimately like to happen: ArchIdea

This architecture allows for a very trivial plugin system (at least in the QWidgets world) and I would very much like to keep that. Not a massive singleton and account for every possible action...

I'd appreciate ideas!

2 Answers

I think this is actually very easy, if you return a QQuickItem from the C++ side. If you do so you can create it with a specific context, in which you can set your "specific hardware controller" as a property

QQmlComponent *qml_controller = new QQmlComponent(qengine, "some_file.qml");
QQmlContext *context = new QQmlContext(); //should probably give a pointer to owning object
context->setContextProperty("controller", pointer_to_hw_cont);

return qml_controller->create(context);

The Loader setSource method have additional parameter you could pass to provide initial value for some property. Something like this:

ComboBox {
   model: controlerFactory.specificHWListModel
   onCurrentTextChanged: {
       var specificHWControler = controlerFactory.getObjectFor( currentText );
       loader1.setSource( 
           specificHWControler.qml_file,
           { "controler": specificHWControler }
       );
   }
}
Loader { 
    id: loader1
}

The specificHWListModel cold be QStringList or some custom QAbstractListModel. And getObjectForcould be just a invokable function.

Q_INVOKABLE QObject* getObjectFor(QString hwName);

The object returned from Q_INVOKABLE function will be managed by QQmlEngine by default if you don't set by the QQmlEngine::setObjectOwnership. Remember to register your SpecificHWControler class to QQmlEngine.

The qml_file SpecificView.ui.qml, should have property controler, and could be edited with Designer:

import SpecificHWControlerModule 1.0
Item {
    property SpecificHWControler controler

}

https://doc.qt.io/qtcreator/quick-connections-backend.html

Related