[Python] Direct access to objects?

This forum is currently in read-only mode.
  • Let's say we have a layout with a Text object named "Text" and create a script in the script editor:

    def sayBoo():
        Text.Text = "Boo!"
    
    sayBoo()
    [/code:3ijtereh]
    
    We place this script under a "start of layout"-event and run the app. The text is set to "Boo!", just like it was intended.
    
    If we now save this script as a module named "boo.py" in the appropriate folder and replace the script in the script editor with
    
    [code:3ijtereh]
    import boo
    
    boo.sayBoo()
    [/code:3ijtereh]
    
    and run it, we get the error "global name 'Text' is not defined". Obviously, the interpreter is not aware anymore, that Text is a reference to the text object. Of course, by extending the function with a parameter textobject and in the code using this reference (textobject.Text = "Boo!") it works again.
    
    But what to do, if there is no input possible? Is there another way to reference an object? The above was just an example, what I really would like to access from within my own module are a few attributes from System, like System.AppPath
    
    The script editor resolves the references on the first code layer correctly, so there must be a way. Can someone point me to the right direction?
  • That's because you can't have cross-module variables in Python.

    The only way (read: very bad hack) to do it is using __builtin__ which is global in all modules:

    boo.py

    import __builtin__
    
    def sayBoo():
        __builtin__.Text.Text = "Boo!"
    [/code:jgr2l8p6]editor:
    [code:jgr2l8p6]
    import __builtin__
    import boo
    
    __builtin__.Text = Text
    boo.sayBoo()
    [/code:jgr2l8p6]
    
    [url=http://stackoverflow.com/questions/142545/python-how-to-make-a-cross-module-variable]See here for reference[/url]
    
    But having to pass around objects like this is [b]very[/b] ugly.
    
    At this point I prefer using parameters:
    
    boo.py
    [code:jgr2l8p6]
    def sayBoo(o):
        o.Text = "Boo!"
    [/code:jgr2l8p6]
    
    editor:
    [code:jgr2l8p6]
    import boo
    
    boo.sayBoo(Text)
    [/code:jgr2l8p6]
  • Thank you very much for the answer

    __builtin__ won't help in this case, because it refers to previously defined identifiers in modules. The objects of Construct are not defined in a module and can't be. Construct's objects are referenced through their names and they can change from session to session. Take the first example:

    def sayBoo():
        Text.Text = "Boo!"
    
    sayBoo()[/code:1n5z6rp3]
    
    If you now rename the text object in Construct to "tBoo", you get the same "global name" error. As soon as you update your script to "tBoo.Text = "Boo!" it works again.
    
    The editor seems to map words to object references on the fly. I hoped I could have access to this mapping too (because 'System' is a built in object that will never change its name it would be easy to access without being too "hacky")
  • Taking Keeper's idea and cutting out the middleman, makes for one step after importing "boo". This is probably the least "hacky" way to go about it, and it keeps the code looking clean.

    boo.py

    def sayBoo():
        Text.Text = "Boo!"[/code:3csgmg65]
    editor:
    [code:3csgmg65]import boo
    boo.Text = Text
    
    boo.sayBoo()[/code:3csgmg65]
    
    And if the Text object is renamed only one line needs to be modified in the editor from
    [code:3csgmg65]boo.Text = Text[/code:3csgmg65] to [code:3csgmg65]boo.Text = tBoo[/code:3csgmg65]
  • And by taking R0J0hound's idea you can pass around all variables using globals()

    boo.py

    def sayBoo():
        vars['Text'].Text = "Boo!"
    [/code:3onb2jrp]
    event
    [code:3onb2jrp]
    import boo
    boo.vars = globals()
    
    boo.sayBoo()
    [/code:3onb2jrp]
    
    You still have to touch the code if you rename your objects but this way you can cycle on them since globals() returns a dict
  • Fantastic guys!

    Thank you so much, globals() is a very good approach. Only "System" isn't referenced, but maybe I find it when I don't look for it

  • You can access System using globals() this way:

    def sayBoo():
        Text = vars['Text']
        System = vars['System']
        Text.Text = "Screen is %d,%d" % (System.DisplayWidth, System.DisplayHeight)
    [/code:37kwdjs9]
  • Try Construct 3

    Develop games in your browser. Powerful, performant & highly capable.

    Try Now Construct 3 users don't see these ads
  • You can access System using globals() this way:

    > def sayBoo():
        Text = vars['Text']
        System = vars['System']
        Text.Text = "Screen is %d,%d" % (System.DisplayWidth, System.DisplayHeight)
    [/code:39u3aevf]
    

    Great! Thank you, Keeper, I'd say this is as close as possible to what I had in mind. Someone should start a wiki about Python with Construct, globals() should be more prominent

  • Glad to be of help

    But remember to organize your code and try to understand/use classes: they can make your life a lot easier!

    boo.py

    class Boo:
        def __init__(self, Text, System):
            self.Text = Text
            self.System = System
        def sayBoo(self):
            self.Text.Text = "Screen is %d,%d" % (self.System.DisplayWidth, self.System.DisplayHeight)
    [/code:3g03cgkv]
    
    event
    [code:3g03cgkv]
    from boo import Boo
    cboo = Boo(Text, System)
    cboo.sayBoo()
    [/code:3g03cgkv]
  • Glad to be of help

    But remember to organize your code and try to understand/use classes: they can make your life a lot easier!

    I'm pretty familiar with Python and the concepts of modules, classes, methods, functions and oop. The background for my question was, that I'm coding a system that might be of help for many Constructors, and I want to establish it with the least effort for them as possible. The complete system shall run with no more than two (visible) scripts:

    Start of layout:

    from fancypkgname import attractivemodulename
    
    myclass = attractivemodulename.coolclass(globals())
    [/code:25apxpgq]
    
    Always:
    [code:25apxpgq]myclass.update()[/code:25apxpgq]
    
    They should not have to alter the code everytime they use it in another project. That was the intention
  • Nice idea!

    You still have to change object names inside your update() if you change them in Construct.

    I guess it's only a matter of taste where to reference the global objects.

    Following your idea I think Construct should append the project path to sys.path automatically so one can write his own modules in the same directory (or directory structure following python standard __init__.py).

    To test the (little) module we discussed in this page I was using this code:

    import sys
    sys.path.append("D:/test")
    [/code:383wvjwf]
    
    Also, a bit OT, is there a way to write Construct plugin using python?
  • Automatically appending to sys.path would be a convenience. You could also generalize it with

    import os
    import sys
    sys.path.append(os.getcwd())
    [/code:1xjyesjw]
    On the other hand, when thinking of later uses by other constructors, it might be better to let them just copy the package to Constructpath\Data\Python. This way, everything one might want to use can be found at a central, general place... I made no decision yet.
    
    Writing plugins with Python? I don't think it's possible. The sdk is for use with Microsoft Visual C++/Studio, and embedding Python through C++ (although possible, [url]http://docs.python.org/c-api/[/url]) would just make it more complicated. But maybe some day one talented programmer creates a python-plugin, a precompiled plugin whose functionality is completely controlled by simple python scripts. I would walk over broken glass if that would help to motivate someone developing it
Jump to:
Active Users
There are 1 visitors browsing this topic (0 users and 1 guests)