Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

How to set a non-global context property for a single QQmlComponent?



  • This was previously asked here, but didn't get any responses: Setting context property for individual QML UI element

    Surely there must be a way to set a context property on a single component without having to set it globally on the entire engine? Indeed, the documentation is silent on this point.

    Help??


  • Moderators

    @patrickkidd I don't think this is possible.

    Assigning stuff to the root context is supposed to be global for all qml files.

    If you want a c++ class that is independent from other aktive instances of your c++ backend and only access it via qml, than you can do that via qmlRegisterType
    If you want it to access an instance from other c++ instances and only access it from specific QML files, than a Singelton is your only way via qmlRegisterSingletonType



  • @J.Hilk said in How to set a non-global context property for a single QQmlComponent?:

    @patrickkidd I don't think this is possible.

    Assigning stuff to the root context is supposed to be global for all qml files.

    If you want a c++ class that is independent from other aktive instances of your c++ backend and only access it via qml, than you can do that via qmlRegisterType
    If you want it to access an instance from other c++ instances and only access it from specific QML files, than a Singelton is your only way via qmlRegisterSingletonType

    I am wondering about exposing object instances, not types, to a single QQmlComponent.


  • Moderators

    @patrickkidd than take a look at his section

    https://doc.qt.io/qt-5/qqmlengine.html#qmlRegisterSingletonType

    an example how to register and access the class - taken from my current project

    //main.cpp
    
    qmlRegisterSingletonType<BluetoothDevice>("BluetoothBackend", 1, 0, "Bluetooth", BluetoothDevice::instance);
    
    //in my qml file
    import BluetoothBackend 1.0
    
    ....
    ListView {
                id:lView
                anchors.fill: parent
    
                model: Bluetooth.devices
                ....
                onClicked:{
                      Bluetooth.connectToDevice(modelData.deviceAddress)
                }
    
    
    //the c++ callback, because the docu is not that great in that instance
    
    static QObject *instance(QQmlEngine *engine, QJSEngine *scriptEngine)
        {
            Q_UNUSED(scriptEngine)
    
            if(BluetoothBackend)
                return BluetoothBackend;
    
            BluetoothBackend = new BluetoothDevice();
            engine->setObjectOwnership(BluetoothBackend,QQmlEngine::CppOwnership);
            return BluetoothBackend;
        }
    


  • @J.Hilk said in How to set a non-global context property for a single QQmlComponent?:

    @patrickkidd than take a look at his section

    https://doc.qt.io/qt-5/qqmlengine.html#qmlRegisterSingletonType

    an example how to register and access the class - taken from my current project

    //main.cpp
    
    qmlRegisterSingletonType<BluetoothDevice>("BluetoothBackend", 1, 0, "Bluetooth", BluetoothDevice::instance);
    
    //in my qml file
    import BluetoothBackend 1.0
    
    ....
    ListView {
                id:lView
                anchors.fill: parent
    
                model: Bluetooth.devices
                ....
                onClicked:{
                      Bluetooth.connectToDevice(modelData.deviceAddress)
                }
    

    I'm sorry, that is still not what I am asking. I am specifically asking how to set a context property that only one component can see. For example, I would set one object as a "dateModel" context property on one component, and different object to the "dateModel" context property on another component.


  • Moderators



  • @patrickkidd As already answered, there is no way to use the context property mechanism to do what you want. However, if you expose a C++ class to QML via qmlRegisterType, you can create an instance of that type in your QML component:

    class MyType: public QObject
    {
        Q_OBJECT
        // properties
        Q_PROPERTY(bool isValid READ isValid WRITE setIsValid NOTIFY isValidChanged)
        ...
    public: //methods
        Q_INVOKABLE void doSomething(...);
        ...
    
    // QML component
    Item {
        MyType {
             id: myType
             isValid: false
             ...
        }
    
        // reference properties and methods as required:
       //    myType.isValid
       //    myType.doSomething(...)
    

    The point is that you have the freedom to do what you want in your C++ object. You could expose a model as a property of it for example.

    Also, in this way you are in control of what is visible in your component. Note that this naturally instantiates an instance of your C++ object per QML component instance, but you have room for manoeuvre in the C++ backend. If, for example, you want what would effectively be a singleton to share between the QML components that reference it you could implement it as a Monostate-like class.



  • @Bob64 @J-Hilk

    You arguments make sense, and it seems clear from the flow chart that setting up a context specific to the component is not possible. Further, it probably is better practice to just create a var property in the component and assign the QAbstractItemModel to it, if That model actually must be created in C++ (for example if it is handed to one or more other components).

    Just for my own understanding, I did get a reply from the Qt-interest mailing list that you can create a sub-context like the following snippet, though this isn't possible with a QQuickWidget at current. Also, to my knowledge there is no way to show an item created with QQmlComponent::create in a widget....I could be wrong though. Doesn't this create a sub-scope as in the original question though?

    QQmlContext *context = new QQmlContext(engine->rootContext());
    QQmlComponent *comp = new QQmlComponent(mySource);
    context->setContextProperty("myModel", myModel);
    comp->create(context);
    


  • I have been pursuing the route of setting a property on the root QQuickWidget component. Qml's property inheritance makes it so that all child components can see that property set on the root, so this works in theory.

    One reason I was avoiding this was that there would be a delay between creating the root component and setting the property and all of the references to the property would fail, particularly if I initialize the root property to null. I imagine the workaround is to initialize the property value to a dummy object or uninitialized object of the same class, and then overwrite it with QQuickItem::setProperty after the Component is initialized from qml source.

    Now, the problem I am running into with this solution is that the dummy objects are not destroyed when I overwrite them with QQuickItem::setProperty. I checked this by starting a C++ timer on them with a print statement. But this could be because I am using PyQt and a reference is still around somewhere (although the C++ object should have been deleted anyway...) or because QObject::startTimer keeps the object around. Or maybe I should just ignore these "leaked" objects anyway.

    At any rate, this looks to me like an important design principle to nail down.


Log in to reply