2
$\begingroup$

Total rewrite of my question

From this post, I've greatly simplified my approach:

bpy.ops.wm.append(
    filepath="Skin.blend",
    directory=(addon_path, "Skin.blend"),
    filename="Skin")
bpy.ops.wm.append(
    filepath="Skin.blend",
    directory=(addon_path, "Skin.blend"),   
    filename="imggen")

Obviously, the problem I have is addon_path there. I have as yet been unable to figure a way of calling the packed blend file from within the addon zip. If I use the OS path to the file it works perfectly.

Update

Trying a different approach now, with this bit of code:


with bpy.data.libraries.load(filepath, link=True) as (data_from, data_to):
    data_to.objects = [name for name in data_from.objects if name.startswith("S")]

Blender now sees the file properly, but it won't load it.

Traceback (most recent call last): File "/path/to/file/addon test.blend/ui template", line 53, in < module >

OSError: load:path/to/file/addon test/skin/Skin.blend failed to open blend file

It's not telling me why it fails, it's just failing.

If I pack it up and try to install it I get this:

File "/home/maker/Downloads/blender-2.93.6-linux-x64/2.93/scripts/modules/addon_utils.py", line 351, in enable
    mod = __import__(module_name)
  File "/home/maker/.config/blender/2.93/scripts/addons/DIPSS/__init__.py", line 53, in <module>
    with bpy.data.libraries.load(filepath, link=True) as (data_from, data_to):
AttributeError: '_RestrictData' object has no attribute 'libraries'

Update I've tried a hundred different things to get the file paths to work and the closest I've got now is this:

skindir = os.path.dirname(os.path.abspath(__file__))
skinmat = "\\Material\\"
skingen = "\\Text\\"

bpy.ops.wm.append(
    filepath="Skin.blend",
    directory=skindir + skinmat,
    filename="Skin")
bpy.ops.wm.append(
    filepath="Skin.blend",
    directory=skindir + skingen,   
    filename="imggen")

This almost works. When I try to run it in my test file it tells me cannot use current file as library When I pack it and try to install the addon, I get something a bit more involved:

File "/home/maker/Downloads/blender-2.93.6-linux-x64/2.93/scripts/modules/addon_utils.py", line 351, in enable
    mod = __import__(module_name)
  File "/home/maker/.config/blender/2.93/scripts/addons/DIPSS/__init__.py", line 74, in <module>
    bpy.ops.wm.append(
  File "/home/maker/Downloads/blender-2.93.6-linux-x64/2.93/scripts/modules/bpy/ops.py", line 126, in __call__
    _BPyOpsSubModOp._view_layer_update(context)
  File "/home/maker/Downloads/blender-2.93.6-linux-x64/2.93/scripts/modules/bpy/ops.py", line 88, in _view_layer_update
    view_layer = context.view_layer
AttributeError: '_RestrictContext' object has no attribute 'view_layer'

I'm not sure why it's even looking for a view layer here. The register section of this script doesn't call for it as far as I can tell, but I may be missing something.

def register():
    from bpy.utils import register_class
    for cls in classes:
        register_class(cls)

    bpy.types.Scene.my_tool = PointerProperty(type=MyProperties)

def unregister():
    from bpy.utils import unregister_class
    for cls in reversed(classes):
        unregister_class(cls)
    del bpy.types.Scene.my_tool


if __name__ == "__main__":
    register()

Update I've managed to make a button in an init file that will append and load the texture, thanks to this question.

Now I'm having trouble doing the same with my other script files. It would be neat if I could have them load the same way, from the same packed blend file, with the same button. But as-is I can't get them to append at all, with their own button. It doesn't spit out an error either, it just doesn't do anything.

USER = Path(resource_path('USER'))
ADDON = "DIPSS"
SKIN = "Skin.blend"
srcPath = USER / "scripts/addons" / ADDON / "assets" / SKIN
srcFile = str(srcPath)

skinmat = "\\Material\\"
skinscript = "\\Text\\"

This is the code that allows me to reference the packed blend file in the addon folder, no matter where blender puts it.

Ignore the order of these snippets, the buttons are in the right order, and I'll rearrange things once I have them working.

class WM_OT_Menu(Operator):
    bl_label = "Load Menu"
    bl_idname = "wm.load_menu"

    def execute(self, context):
        text_main= bpy.data.texts['menu_script.py']
        context_c = context.copy()
        context_c['edit_text'] = text_main
        bpy.ops.text.run_script(context_c)
        return {'FINISHED'}
    
    
    
class WM_OT_Mat(Operator):
    bl_label = "Load Mat"
    bl_idname = "wm.load_mat"

    def execute(self, context):
        bpy.ops.wm.append(
        filepath="Skin.blend",
        directory=srcFile + skinmat,
        filename="Skin",
        set_fake=True)
        return {'FINISHED'}
        
class WM_OT_Script(Operator):
    bl_label = "Load Scripts"
    bl_idname = "wm.load_script"
        
    def execute(self,context):
        bpy.ops.wm.append(
        filepath="Skin.blend",
        directory=srcFile + skinscript,
        filename="menu_script.py, imggen.py")
        return {'FINISHED'}

These are the buttons. Load Mat now works perfectly. Load Scripts doesn't do anything at all, not even spit an error. And of course, Load Menu is supposed to invoke one of the appended scripts, which it can't because the script isn't being appended.

$\endgroup$
2
  • $\begingroup$ The problem is in menu_script and doesn't look like it has to do with multiple files. You can't access bpy data within register(). You should only register classes and stuff. $\endgroup$
    – scurest
    Commented Jun 30, 2022 at 23:36
  • $\begingroup$ menu_script.py works perfectly on its' own though. Do these scripts try to double-up on importing functions if you run them in a sequence? $\endgroup$ Commented Jun 30, 2022 at 23:40

2 Answers 2

1
$\begingroup$

I figured it out. Turns out my idea was right, but I couldn't do both scripts in one snippet. I also compressed it into a single button that does it all in order.

USER = Path(resource_path('USER'))
ADDON = "DIPSS"
SKIN = "Skin.blend"
srcPath = USER / "scripts/addons" / ADDON / "assets" / SKIN
srcFile = str(srcPath)

skinmat = "\\Material\\"
skinscript = "\\Text\\"

# ------------------------------------------------------------------------
#    Operators
# ------------------------------------------------------------------------

class WM_OT_Mat(Operator):
    bl_label = "Begin"
    bl_idname = "wm.load_mat"

    def execute(self, context):
        bpy.ops.wm.append(
        filepath="Skin.blend",
        directory=srcFile + skinmat,
        filename="Skin",
        set_fake=True)
        
        bpy.ops.wm.append(
        filepath="Skin.blend",
        directory=srcFile + skinscript,
        filename="menu_script.py",
        set_fake=True)
        
        bpy.ops.wm.append(
        filepath="Skin.blend",
        directory=srcFile + skinscript,
        filename="imggen.py",
        set_fake=True)
        
        text_main= bpy.data.texts['menu_script.py']
        context_c = context.copy()
        context_c['edit_text'] = text_main
        bpy.ops.text.run_script(context_c)
        return {'FINISHED'}
$\endgroup$
0
$\begingroup$

Just put your .blend file and this code in a folder, name the code 'init.py' and zip the folder. You need to rename some stuff on the script accordingly to your file.

bl_info = {
    "name": "Object Append Add-on",
    "description": "...",
    "author": "Suzanne",
    "version": (0, 0, 1),
    "blender": (3, 2, 0),
    "location": "View3D > Add",
    "wiki_url":"...",
    "category": "Add Mesh",
}

import bpy
from os import path


my_dir = path.dirname(path.realpath(__file__))
blend_file = path.join(my_dir, "suzanne.blend")
print(blend_file)

class ObjectAppend(bpy.types.Operator):
    bl_idname = "add.suzanne"
    bl_label = "Suzanne"
    bl_description = ("Adds a new Suzanne to your scene")
    bl_options = {'REGISTER', 'UNDO'}

    #adds all objects starting with the name suzanne to the scene
    def execute(self, context):
        bpy.ops.object.select_all(action='DESELECT')             #deselect all objects    
        with bpy.data.libraries.load(blend_file) as (data_from, data_to):
            data_to.objects = [name for name in data_from.objects if "Suzanne" in name]
        for obj in data_to.objects:
            context.collection.objects.link(obj)
            context.view_layer.objects.active = obj              #select appended objects
            obj.select_set(True)
        bpy.ops.view3d.snap_selected_to_cursor(use_offset=False) #snap all objects to 3D cursor
        return{'FINISHED'}

def draw(self, context):
    layout = self.layout
    layout.operator("add.suzanne", text="Suzanne", icon="MESH_MONKEY")


def register():
    bpy.utils.register_class(ObjectAppend)
    bpy.types.VIEW3D_MT_add.append(draw)


def unregister():
    bpy.utils.unregister_class(ObjectAppend)
    bpy.types.VIEW3D_MT_add.remove(draw)

if __name__ == "__main__":
    register()
$\endgroup$

You must log in to answer this question.

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