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 Answer 1

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$
3
  • $\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$
    – Fox
    Commented Sep 26, 2019 at 13:12
  • $\begingroup$ @DB3D Perhaps the script you're trying to execute simply has an error? $\endgroup$ Commented Sep 26, 2019 at 15:22
  • $\begingroup$ No because be its working fine when pushing on then run script button $\endgroup$
    – Fox
    Commented Sep 26, 2019 at 15:29

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .