Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Working with anonymous scripts #4

Closed
kabukunz opened this issue Mar 4, 2018 · 3 comments
Closed

Working with anonymous scripts #4

kabukunz opened this issue Mar 4, 2018 · 3 comments

Comments

@kabukunz
Copy link

kabukunz commented Mar 4, 2018

Just wanted you to know your addon works even with simple scripts, albeit in a non-common manner.

In Blender, from within the script window, use template / python / external script stub. It creates a script which can launch an external script set in the same dir as the blend file.

# This stub runs a python script relative to the currently open
# blend file, useful when editing scripts externally.

import bpy
import os

# Use your own script name here:
filename = "my_script.py"

filepath = os.path.join(os.path.dirname(bpy.data.filepath), filename)
global_namespace = {"__file__": filepath, "__name__": "__main__"}
with open(filepath, 'rb') as file:
    exec(compile(file.read(), filepath, 'exec'), global_namespace)

Create a my_script there, start the debugger, and attach to it with visual studio. Set a breakpoint anywhere. Run.

This is the only way I found until now to run such simple scripts. Addons can be tracked as you explained, and so operators and such similar things.
While simple "hello there" script run from within the text window in Blender apparently cannot, probably because are anonymous... or use some indirect mechanism which I don't know.
Really you only have to update the docs...

@AlansCodeLog
Copy link
Owner

AlansCodeLog commented Mar 5, 2018

I honestly didn't give any thought to running plain scripts, but I just tested this and a few other ways I would have approached it and:

If you're going to be putting the file next to blender, you can actually get the debugger to pause by just opening the script as a text block and running it (though of course you have to be refreshing changes all the time, unlike with your script). In fact, it doesn't have to be placed next to blender, opening any script which is also open in the folder VS Code is open to will work. BUT for some reason it doesn't pause correctly, that is, it pauses, but it doesn't show variables when stepping through the code. Not sure why, I'd have to look into how Blender handles running external scripts.

What's interesting though is your code CAN take an absolute path (all forward slashes though) and it works correctly, pausing and showing variables. So it might be possible to allow the user to specify some path for plain scripts, then have a dropdown to run them.

It might even be possible to take this further and watch for changes, I'd have to look into how that's done though. Actually, I just realized this isn't necessary. 🤦‍♂️

Then I think there's other ways to run external scripts (like importing them as modules). Best way does indeed seem to be execute them.

When I have some time I'll investigate further. Am working on this now.

@AlansCodeLog
Copy link
Owner

I have closed this for now because I have linked to your workaround in the readme and have no time to look into integrating a better one into the addon for now. Will re-open if I do.

For anybody reading this, suggestions on how to approach this or pull requests are welcome.

AlansCodeLog added a commit that referenced this issue Sep 9, 2018
- Fixes #8
- Improved python path searching code.
- Added documentation regarding usage with virtual environments.
- Added link to single file scripts workaround issue. Closes #4
@AlansCodeLog
Copy link
Owner

Since I've had a few people stumble across this problem and some have asked how to turn scripts into addons, I thought I would detail that workaround as well.

I have created an example addon script (should be saved as __init__.py in an addon folder, inside the folder detailed in the documentation), demonstrating the three ways I know how.

One is just to inline the script, and move imports to the top.

Another is to execute a script, something similar to the OP's workaround (this is the only way changes will be auto detected, since it's re-read on each run). In the example the filepath is set to be the addon folder + script name. But you should be able to change this to an absolute path if you want.

The third is import it as a module by wrapping it in a function, for example, in your script:

import bpy
# move imports here
def my_script:
    # you script

Example Addon:

# the minimum info needed to run an addon
bl_info = {
   "name": "My Scripts",
   "category": "Testing",
}

import bpy
from . import script_execute # script_execute should be the file name
# any imports from the inline script should go here

class MyScriptInline(bpy.types.Operator): #this name does not really matter, it's only to register the commands
    """Tooltip"""
    bl_idname = "my_scripts.run_my_script_inline"
    # my_scripts can be whatever, it's like a namespace under which to put all your commands
    # it can also be something that already exists (under bpy.ops),
    # e.g. if your script is related to objects: object.run_my_script
    bl_label = "My Script Inline" #this is the name that will come up in the search or menus if you put it in a menu

    def execute(self, context):
      #script here inline
      return {'FINISHED'}

class MyScriptExecute(bpy.types.Operator):
    """Tooltip"""
    bl_idname = "my_scripts.run_my_script_execute"
    bl_label = "My Script Execute" 

    def execute(self, context):
        filename = "script_execute.py" #name to script in same folder as addon
        filepath = os.path.join(os.path.dirname(os.path.realpath(__file__)), filename)
        with open(filepath, 'rb') as file:
           global_namespace = {"__file__": filepath, "__name__": "__main__"}
           exec(compile(file.read(), filepath, 'exec'), global_namespace)
        return {'FINISHED'}

class MyScriptImport(bpy.types.Operator):
    """Tooltip"""
    bl_idname = "my_scripts.run_my_script_import"
    bl_label = "My Script Import" 

    def execute(self, context):
       script_execute.my_script()

#if you add more, don't forget to register/unregister the classes!
def register():
    bpy.utils.register_class(MyScriptInline)
    bpy.utils.register_class(MyScriptExecute)
    bpy.utils.register_class(MyScriptImport)
def unregister():
    bpy.utils.unregister_class(MyScriptInline)
    bpy.utils.unregister_class(MyScriptExecute)
    bpy.utils.unregister_class(MyScriptImport)

if __name__ == "__main__":
    register()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants