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

Dynamic Property Updates : It works, room for improvement/refinement?



  • I was looking into whether or not QObject dynamic properties could be auto updated in QML properties. I was looking for the possibility of dynamic properties in QML objects that could update when a dynamic property changes on a QObject. So I pieced together the following:
    main.qml:

    import QtQuick 2.9
    import QtQuick.Window 2.2
    import QtQuick.Controls 2.5
    
    Window {
        visible: true
        width: 640
        height: 480
        title: qsTr("Testing Dynamic Object Properties")
    
        property var plist: ({})
    
        function createProperties(){
            plist["one"] = 0
            plist["two"] = "test"
            plist["three"] = 0.24
            plist["four"] = 1
    
            for(var key in plist) {
                dynamicObj.setProperty(key, plist[key])
            }
        }
    
        function changeProperties(){
            dynamicObj.setProperty("one",5)
        }
    
        Connections {
            target: dynamicObj
            onPropertyChanged: {
                console.log(name)
                plist[name] = dynamicObj.property(name)
                plistChanged() // causes entire structure to be reevaluated and all dependent properties to be updated
            }
        }
    
        Component.onCompleted: {
            console.log(dynamicObj.objectName)
            createProperties()
            //console.log("change things")
            //changeProperties()
        }
    
        Text{
            id: text1
            width: 50
            height: 20
            y:0
            color: "black"
            text: plist["one"]
        }
        Text{
            id: text2
            width: 50
            height: 20
            y:20
            color: "black"
            text: plist["two"]
        }
        Text{
            id: text3
            width: 50
            height: 20
            y:40
            color: "black"
            text: plist["three"]
        }
        Text{
            id: text4
            width: 50
            height: 20
            y:60
            color: "black"
            text: plist["four"]
        }
    
        Button {
            id: but_change
    
            anchors.top: text4.bottom
            text: "change"
    
            onClicked: {
                var num = Math.round(Math.random() * 100)
                console.log(num)
                dynamicObj.setProperty("one",num)
            }
        }
    }
    

    main.cpp:

    #include <QGuiApplication>
    #include <QQmlApplicationEngine>
    #include <QQmlContext>
    
    class DynObj : public QObject
    {
        Q_OBJECT
    
    public:
        Q_INVOKABLE bool setProperty(QString name, const QVariant &value) {return QObject::setProperty(name.toUtf8(),value);}
        Q_INVOKABLE QVariant property(QString name) const {return QObject::property(name.toUtf8());}
    
    private:
        bool event(QEvent *event)
        {
            if (event->type() == QEvent::DynamicPropertyChange)
            {
                QDynamicPropertyChangeEvent *const propEvent = static_cast<QDynamicPropertyChangeEvent*>(event);
                QString propName = propEvent->propertyName();
                emit propertyChanged(propName);
            }
    
            return QObject::event(event);
        }
    
    signals:
        void propertyChanged(QString name);
    
    };
    
    int main(int argc, char *argv[])
    {
        QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    
        QGuiApplication app(argc, argv);
    
        QQmlApplicationEngine engine;
    
        DynObj dynamicObj;
        engine.rootContext()->setContextProperty("dynamicObj", &dynamicObj);
        dynamicObj.setObjectName("dynamicObj");
    
        engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
        if (engine.rootObjects().isEmpty())
            return -1;
    
        return app.exec();
    }
    
    #include "main.moc"
    

    To work it creates a C++ QObject derived object that adds a couple of invokable methods to allow adding dynamic properties via QML. It also add a signal that fires whenever a property is updated. This is done by capturing the property changed event.

    In the QML I create a map of "dynamic" properties represented by plist. I capture the signal from the C++ object and have it update the plist when a property changes in the C++ object. This then calls the function plistChanged which notifies all the QML objects that depend upon values in the plist to be updated.

    To initiate a property change I set the property on the C++ object. The event fires on the object, which in turn updates the plist and forces the QML objects to update.

    The biggest limitation with this I think is that as plist grows updates will affect updates to more and more objects. So I don't think it scales well. I also don't know if this is any better than just using a list model or database backend. I wrote this as more of a "can it be done?" Though it might be useful for something.

    Any thoughts for improvement/refinement? I am very well aware it could be a misguided approach to begin with. Muahahahahaha!


Log in to reply