How to get eval() to operate in the scope of QML element



  • If I have some JS code as text and I call it in eval(), I can then use the functions defined by this statement only in the scope of where the eval() occurred. As far as I can tell, this means that any new JS definitions created from the eval are invisible to the rest of QML. Consider this QML signal handler:

    @
    Item {
    id: testitem
    onThesourceChanged: {
    eval(thesource)
    testfunction()
    }
    }@

    The testfunciton() was defined in the text "thesource" and does indeed work; but it is not in the scope of the QML element and therefore cannot be called ever again, as far as I can see. I've tried various ideas like putting the eval() in a function, or inside Component.onCompleted, etc, but I can't seem to figure out how to get anything created by the eval() to be recognized as part of the parent QML element.

    For example I want to be able to call testitem.testfunction() -- but it is not defined outside the scope of this handler.

    Can anyone assist?



  • Hi, maybe a little bit far fetched, but the function might be garbage collected after you call it, since you just define it inside your "onThesourceChanged" slot and call it once, it seems like a local function in a function!? Just an idea :)

    if you use eval your get somethging like this:
    @
    Item {
    id: testitem
    onThesourceChanged: {
    var testfunction = function() { ... } // after eval -> local function ?
    testfunction()
    }
    }
    @



  • You are probably right, but the question is, how to get the definitions that appear inside the text that is evaluated with eval() to actually be attached to a QML element and persist?



  • Well simple solution would be so save a reference to the function, so you can call it from outside, e.g.
    @
    Item {
    id: testitem
    property var testfunction // use this property to save the function you generate in your eval
    onThesourceChanged: {
    eval(thesource)
    testfunction()
    }
    }
    @
    I can't think of any easier way at the moment, but if you only have once function you can do it like this, it gets a little more complicated if you generate multiple dynamic functions...
    you can set the property from the source code your provide to eval or better return it in the source code ans use it like
    @
    testfunction = eval(thesource)
    @
    that should work, I have no idea if there is a way to define dynamic functions or properties in QML. Maybe you can research that or somebody else knows that, but I am not aware of something like that.



  • I have many functions, and their names and quantity will vary; I'm essentially loading a whole JS program at runtime via the eval. So I can't assign individual vars like this, though it is a good idea.



  • Ok then I have another idea, if you load a whole script do you have control over it somehow? I mean you could wrap it inside a JS object and assign that to a property?
    So same code as in my last post, just the property name changed
    @
    Item {
    id: testitem
    property var myFunctions // use this property to store your object with all functions
    onThesourceChanged: {
    myFunctions = eval(thesource)
    myFunctions.testfunction()
    }
    }
    @
    and your "thesource" looks something like
    @
    return {
    testfunction: function() { ... },
    testfunction2: function() { ... },
    testfunction3: function() { ... }
    ...
    }
    @

    Still this sounds a little weird, but I really have no idea if you can create dynamic functions property with QML. What you need is a JS lib maybe, like if you create a real .js file in QML and import it like
    @
    import "YourFunctions.js" as YourFunctions
    @
    again no idea if it would be possible to load a dynamically generated JS file like that, but that would be the best answer, if possible. And not assign the functions to a QML Item.



  • A var needs to be in JSON format, right? If you have a JS file with normal standalone definitions, those will not be assigned to JSON keys, so you can't assign the entire file to a var. I wish you could, though.

    My JS file is just a normal JS program. I cannot wrap it inside anything or assign each definition to a JSON key. I can import it in QML fine, but I want to be able to refresh this import dynamically, and using eval() seems to be one way to do this, if only I can get the scope to work out.



  • I don' think the "property var" needs to be in JSON format, since Qt 5 at least it can be any JavaScript value you can assign to a local "var". before Qt 5 you couldn't e event assign a function to a "property" as far as I know, but I have done that in my app (Qt 5.2.1).
    Well maybe someone else has an idea with your problem, if you can't use it like this. :/



  • It would be awesome if the import statement at the top of a QML file could just be reloaded at runtime. I can't find any way to do that either. It would solve my issue.



  • I found the solution. Using Qt.include() allows for dynamic reloading of a javascript file at runtime, even if that javascript file has changed.


Log in to reply
 

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