QML syntax for QVariantMaps?



  • I'm using QML as a general scene language for a node-graph type of project I'm doing. This syntax works OK:

    @
    Scene {
    contents: [
    SomeNode {
    name: "node1"
    values: [
    Value {
    name: "v1"
    value: 1
    },
    Value {
    name: "v2"
    value: 2
    }
    ]
    }
    ]
    }
    @

    Where Scene.qml defines the "contents" property (as list), and SomeNode.qml defines the "name" (string) and "values" (list<Value>) property.

    What I really want is that "contents" and "values" be of type map<Value> (which obviously doesn't exist in QML). They would then be available in Javascript as an Object, and in C++ as a QVariantMap. In QML, it might look like this:

    @
    Scene {
    contents: {
    "node1": SomeNode {
    values: {
    "v1": Value {
    name: "v1"
    value: 1
    },
    "v2": Value {
    name: "v2"
    value: 2
    }
    }
    }
    }
    }
    @

    This seems like a natural and useful extension to QML. Or am I smoking crack? :-)

    cheers,
    -Mark



  • JavaScript objects are converted to QVariantMaps when assigned to variant properties.
    That is, if you have:
    @
    Item {
    property variant myMap: { "v1": { "name": "v1", "value": 1 }, "v2" : {"name": "v2", "value": 2} } // or whatever
    }
    @
    And then you access that property from C++ via QObject::property(), it will be a QVariantMap.

    The problem (I believe) is that you've defined contents and values as a list property (I believe), and hence you are assigning those properties array values.

    Or have I misunderstood your question?

    Cheers,
    Chris.

    [Wrapped code in @ tags; mlong]



  • Sorta. The difference is that in your example, myMap can only contain basic types, not QObject-derived types. Also, changing a value inside myMap won't generate a notification signal.

    I thought about it some more, and my conclusion is that I'll need to think about it some more. :-) QtQuick is a weird and wonderful blend of static and dynamic-ness, and I'm still trying to wrap my head around it.

    cheers,
    -Mark



  • Ah, I see.

    Yeah, in QtQuick 2 a lot of those problems were solved with the addition of "var" properties (which store JS references). You can store basically anything in them (references to JS arrays, functions, objects, qobject-derived instance references, etc). But, they still don't notify if one of the values changes - we had an implementation which allowed construction of a "bindable/notifying" js object, but in practice its performance was so bad that we decided against including it. The plan is to add support for it in the future (there is a task on the public bugtracker about bindable js objects) but no ETA on when it'll be complete.

    Cheers,
    Chris.



  • Well, here's what I'm trying to do. Maybe there's an idiomatic approach that I'm missing?

    There are Nodes and Params. Nodes do things, like raise a number to a power, or compute the BRDF of a surface. Params define the inputs and outputs of Nodes, and have associated metadata for use by the UI presenting them.

    For example, say an Pow raises "base" to "expo":

    @
    // Pow.qml
    import QtQuick 1.1

    Node {
    label: "Power"

    params: [
        Param {
            id: baseParam
            name: "base"
            label: "Base"
        },
        Param {
            id: expoParam
            name: "expo"
            label: "Exponent"
        },
        Result {
            id: result
            name: "result"
            display: "hidden"
        }
    ]
    property alias base: baseParam.value
    property alias expo: expoParam.value
    property alias result: resultParam.value
    

    }
    @

    One would then instantiate a Pow object like so:

    @
    Pow {
    base: 10
    expo: 2
    }
    @

    (I'm ignoring the actual computation of result for now. Insert hand-waving.)

    Querying the "result" property would give you 10^2. Pretty useless, but if you have multiple nodes, you can connect them together:

    @
    Pow {
    id: pow1
    base: 10
    expo: 2
    }
    Pow {
    id: pow2
    base: pow1.result
    expo: pow1.result
    }
    @

    A rather useless example, but you can see that pow2.result will magically evaluate (10^2)^(10^2).

    My problem is that I want Params to be somewhat dynamic, for example:

    @
    // Add.qml
    Node {
    label: "Add Numbers"

    params: [
        Repeater {
            id: vParam
            model: 1
            Param {
                name: "v" + index
                label: "Value " + index
            }
        },
        Param {
            id: resultParam
            name: result
            display: "hidden"
        }
    ]
    
    property alias count: vParam.model
    property alias v1: vParam.????
    property alias v2: vParam.????
    ...
    property alias vN: vParam.????
    property alias result: resultParam.value
    

    }
    @

    And after instantiation:

    @
    // ...

    Add {
    id: add1
    count: 3
    v1: someOtherNode.result
    v2: etc.result
    v3: 25
    }
    @

    This dynamism is a recurring motif in the project I'm planning, so I'm trying to find a good idiom to represent it using QML. I'm not ruling out writing my own QtDeclarative classes with custom parsers (a la ListElement) if that's what it takes. But even if that's necessary, I'd rather build on top of best-practices than beat QML into submission with a hammer. :-)



  • I spoke to Michael Brasser about this briefly today. I'm not sure if I fully understood what you're trying to achieve, but basically I came up with the following:

    @
    // Add.qml - provides the "Add" node implementation
    import QtQuick 2.0
    Item {
    id: addElement

    // all node types must have the following three properties
    property bool isANode: true
    property var inputs
    property var result
    
    // this function will be the same for all node/operation types.
    onInputsChanged: {
        // disconnect all current connections.
        onAnyInputResultChanged.disconnectEverything(); // not sure what the "actual" api is to do this...
    
        // create new connections.
        for (var i = 0; i &lt; inputs.length; ++i) {
            var currInput = inputs[i];
            if (currInput.isObject()) {
                if (currInput.isANode) {
                    currInput.onResultChanged.connect(inputResultChanged);
                }
            }
        }
    
        // update result.
        inputResultChanged();
    }
    
    // this function will be different for each node/operation type.
    // the Add node simply adds up the various inputs' results / values,
    // and exposes the result via its result property.
    function inputResultChanged() {
        var tmp = 0;
        for (var i = 0; i &lt; inputs.length; ++i) {
            var currInput = inputs[i];
            if (currInput.isObject()) {
                if (currInput.isANode) {
                    tmp += currInput.result.valueOf(); // grab the node's result as a number.
                } else {
                    tmp += currInput.valueOf(); // convert random object to number.
                }
            } else {
                tmp += currInput; // must be a number or primitive already.
            }
        }
    
        result = tmp; // will cause notify signal to be emitted.
    }
    

    }
    @

    That way, at run-time, you can dynamically create Add nodes, and modify the input nodes as required. When the input nodes change, or when the result property of any input nodes change, the result of the node will be automatically updated.

    The one "tricky" bit is the "disconnect all current connects to the auto-update function on change" (prior to rebuilding it) -- I'm not sure what the API is for that, but I'm fairly certain it's possible (there's a notify list or signal connection list there somewhere, which can be updated).

    Cheers,
    Chris.



  • Thanks Chris. I haven't had a chance to build QtQuick 2.0 yet, but from your example, it looks like the new var type is exactly what I was looking for.

    cheers,
    -Mark


Log in to reply
 

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