1
$\begingroup$

I made a little add-on that sync a script.py filepath to a text target inside of Blender, it's quite handy for me because I like Sublime text editor more than the Blender's native text editor.

bl_info = {
    "name" : "Script-LiveLink [BD3D]",
    "author" : "BD3D",
    "description" : "LiveLink for script",
    "blender" : (2, 80, 0),
    "location" : "Operator",
    "warning" : "",
    "category" : "Generic"
}

#pep8compliant..ect..

import bpy, os,functools
from bpy.types import Menu, Panel, Operator, PropertyGroup, Operator, AddonPreferences, PropertyGroup
from bpy.props import StringProperty, IntProperty, BoolProperty, FloatProperty, EnumProperty, PointerProperty
C = bpy.context

#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-

def execute_check(G_path,G_name): #TIMER
    if bpy.context.scene[G_name+" check"] == False:
        print(G_name + ' checking aborted')
        return None
    if bpy.context.scene[G_name] != os.path.getmtime(G_path):
        F = open(G_path)
        will_exec = False
        if "EXECUTE" in F.readline():#EXECEXEC
            will_exec = True#EXEC
        F.close()#i just lose the first line so i need to restart
        F = open(G_path)
        bpy.data.texts[G_name].clear()         #clear all text
        bpy.data.texts[G_name].write(F.read()) #paste text from G_path
        if will_exec == True:#EXEC
            #exec(bpy.data.texts[G_name].as_string()) #wont work on addon reg... i tried a lot of other exec method, i think we need bpy.ops.text.run_script()
            exec(compile(open(G_path).read(), G_path, 'exec'))
        F.close()
        bpy.context.scene[G_name] = os.path.getmtime(G_path)
    #print('check')
    return 0.5


class SCR_OT_link(bpy.types.Operator):
    bl_idname = "scr.link"
    bl_label = ""
    bl_description = ""
    index : bpy.props.IntProperty() 
    def execute(self, context):
        index = self.index
        G = bpy.context.scene.SCR_OT_group
        if index == 1:
            G_path = G.path_01
            G_targ = G.target_01
        elif index ==2:
            G_path = G.path_02
            G_targ = G.target_02
        elif index ==3:
            G_path = G.path_03
            G_targ = G.target_03

        G_name = os.path.basename(G_path)
        G_targ.name = G_name
        bpy.context.scene[G_name]          = os.path.getmtime(G_path)
        bpy.context.scene[G_name+" check"] = True
        print("starting timer")
        bpy.app.timers.register(functools.partial(execute_check,G_path,G_name), first_interval=0.5)
        return {'FINISHED'}


class SCR_OT_stop_link(bpy.types.Operator):
    bl_idname = "scr.stop_link"
    bl_label = ""
    bl_description = ""

    index : bpy.props.IntProperty() 
    def execute(self, context):
        index = self.index
        G = bpy.context.scene.SCR_OT_group
        if index == 1:
            G_path = G.path_01
        elif index ==2:
            G_path = G.path_02
        elif index ==3:
            G_path = G.path_03

        G_name = os.path.basename(G_path)
        bpy.context.scene[G_name+" check"] = False
        return {'FINISHED'}

#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-


class SCR_OT_group(bpy.types.PropertyGroup): #not needed, could do with one storage 
    path_01   : StringProperty(name=" ",subtype='FILE_PATH',default=r"‪")
    target_01 : PointerProperty(type=bpy.types.Text)

    path_02   : StringProperty(name=" ",subtype='FILE_PATH',default=r"‪")
    target_02 : PointerProperty(type=bpy.types.Text)

    path_03   : StringProperty(name=" ",subtype='FILE_PATH',default=r"‪")
    target_03 : PointerProperty(type=bpy.types.Text)

class SCR_PT_panel(Panel):
    bl_space_type = 'TEXT_EDITOR'
    bl_region_type = 'UI'
    bl_category = "Text"
    bl_label = "Live-Link"

    def draw(self, context):
        layout = self.layout

        row = layout.column(align=True)
        row.prop(bpy.context.scene.SCR_OT_group, "path_01",text="")
        row.prop(bpy.context.scene.SCR_OT_group, "target_01",text="")
        rowrow = row.row(align=True)
        rowrow.operator(SCR_OT_link.bl_idname, text="Start Live-Link",icon="PASTEDOWN").index       = 1
        rowrow.operator(SCR_OT_stop_link.bl_idname, text="Stop Live-Link",icon="PANEL_CLOSE").index = 1

        layout.separator()

        row = layout.column(align=True)
        row.prop(bpy.context.scene.SCR_OT_group, "path_02",text="")
        row.prop(bpy.context.scene.SCR_OT_group, "target_02",text="")
        rowrow = row.row(align=True)
        rowrow.operator(SCR_OT_link.bl_idname, text="Start Live-Link",icon="PASTEDOWN").index       = 2
        rowrow.operator(SCR_OT_stop_link.bl_idname, text="Stop Live-Link",icon="PANEL_CLOSE").index = 2

        layout.separator()

        row = layout.column(align=True)
        row.prop(bpy.context.scene.SCR_OT_group, "path_03",text="")
        row.prop(bpy.context.scene.SCR_OT_group, "target_03",text="")
        rowrow = row.row(align=True)
        rowrow.operator(SCR_OT_link.bl_idname, text="Start Live-Link",icon="PASTEDOWN").index       = 3
        rowrow.operator(SCR_OT_stop_link.bl_idname, text="Stop Live-Link",icon="PANEL_CLOSE").index = 3

        layout.separator()

        text = layout.box().column(align=True)
        text.label(text='if "EXECUTE" is in the first line of your script', icon='INFO')
        text.label(text='it will Run the script after the Sync')


#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-

sc_classes = {
    SCR_OT_group,
    SCR_PT_panel,
    SCR_OT_link,
    SCR_OT_stop_link,
}

def register():
    for cls in sc_classes:
        bpy.utils.register_class(cls)
    bpy.types.Scene.SCR_OT_group = bpy.props.PointerProperty(type=SCR_OT_group)

def unregister():
    for cls in sc_classes:
        bpy.utils.unregister_class(cls)
    del bpy.types.Scene.C_Slots_settings

if __name__ == "__main__":
    register()

I also made a function that detects if the there's "EXECUTE" in the first line of the script. if it is so, the script will automatically be executed inside of Blender. so I can execute a script from within my text editor easily.

enter image description here

The only problem is: scripts that require a context will not work at all.

Somehow there's no context? So here's my question:

How to run a script from outside of Blender?

Or more precisely:

How to create an artificial context?

I tried multiple script execution methods like exec(compile(open(G_path).read(), G_path, 'exec')) but it never worked... Maybe using bpy.ops.text.run_script() can resolve the problem? But again, it needs a context, and an automatic change editor to the correct text? I don't know - I'm lost...

$\endgroup$
1
$\begingroup$

You could maybe look into bpy.ops.script.python_file_run() See the docs. I think it should be possible to just run the script without loading it to the Text Editor. I have not tested it though.

$\endgroup$
  • $\begingroup$ thanks for the correction. No, python_file_run, will also give me AttributeError: 'Context' object has no attribute 'object' error, due to a context error. And indeed i can run a exec a filepath instead of a text. but in both case it give me a context error. $\endgroup$ – DB3D Sep 26 at 13:12
  • $\begingroup$ @DB3D Perhaps the script you're trying to execute simply has an error? $\endgroup$ – Robert Gützkow Sep 26 at 15:22
  • $\begingroup$ No because be its working fine when pushing on then run script button $\endgroup$ – DB3D Sep 26 at 15:29

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.