4
$\begingroup$

I have a handler scene_update_pre, and I need to run a script when a certain bone is selected. The script is saved as an internal text data block myscript.py.

The problem is that I can't use any derivative of the context override method:

override = {'edit_text': bpy.data.texts['myscript.py']}
bpy.ops.text.run_script(override)

Because it's done in a scene_update_pre handler it will crash Blender, correct?

What other methods are there to run an internal text data block?

bpy.data.texts["myscript.py"]    #.run_script() #?
$\endgroup$
  • 1
    $\begingroup$ Note, while this is possible to do, You're almost certainly better off importing myscript then running a function which it defines. $\endgroup$ – ideasman42 May 16 '14 at 6:20
4
$\begingroup$

Don't run the script directly, instead, you can import the textblock as a python module, and execute a function it defines.

import myscript
myscript.main()

There are some caveats:

  • The script name must be unique to avoid colliding with existing Python modules.
  • You will have to define some function in the module to run.

But the advantages are:

  • Faster (no need to re-parse the script every time), (the compiled bytecode is kept between executions).
  • More flexible since you can pass in arguments if you like, and have multiple functions too.
  • The ability for variables to persist between executions (the module could contain a counter for example that increments each time. or values could be cached to avoid re-evaluation each execution).
    This is also something to watch out for, in case you accidentally re-use global lists, dicts etc...
  • More useful exception messages (__file__, __module__ for example will be defined)
| improve this answer | |
$\endgroup$
  • $\begingroup$ Thank you, I have marked this as the correct answer as its more likely that any one searching is going to want this solution. $\endgroup$ – MCHammond May 16 '14 at 21:28
3
$\begingroup$

It doesn't sound like a good idea to run an embedded text datablock as script on every scene update, since it occurs like 30 times per second.

Anyway, you could avoid an operator call by using:

exec(bpy.data.texts['myscript.py'].as_string())

Not sure if this is any better however. Especially calls like Scene.update() can make the interface non-reactive, so better throttle the number of calls. Or use a modal timer operator, with a timer interval of e.g. 0.5 seconds.

| improve this answer | |
$\endgroup$
  • $\begingroup$ Thank you, it will only run once the bone is deselected after the script is run. $\endgroup$ – MCHammond May 15 '14 at 22:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.