Issues Working with Multi-File Plugins

Hey, i’ve recently started trying to work with multiple files after finding my one long script was becoming too hard to edit. I understand that it requires one main file (init) to import all the other ‘modules’, but apart from that i feel kind of lost on how i should be designing a multi-file script. The information on the BlenderWiki seems out of date as that solution didn’t work for me, and the Python wiki felt rather confusing, and didn’t seem to have much relevant information that applied directly to scripting Blender Plugins - i’ve had to use something like this to have the plugin successfully load:

from .update import *
from .user_interface import *
from .set_data import *
from .draw_stroke import *
from .edit_data import *
from .export_data import *

Using this, the UI loads and everything seems fine, but pretty much all the functions don’t work, as each module has lots of references to operators from other modules, or variables from init. Although I think the variables aren’t having any issues, despite including the above code format for importing modules into other modules, one module can’t find operators in another. How do other people structure their multi-file plugins?

(heres my current code in-case anyone wants to look at it - Download)

Thanks a lot for the info, this and your add-on has really cleared up how I need to organise it. I was aware the mass importing of classes from another file was frowned upon, but it was for testing purposes so I didn’t have to worry about using the right names before getting the multi-file script to work properly.

I’m currently having one more issue though that seems really odd. Ive listed the modules I need to register and unregister like this:

def register():    
    properties.register()
    user_interface.register()
    events.register()
    draw_stroke.register()
    export_data.register()

def unregister():
    properties.unregister()
    user_interface.unregister()
    events.unregister()
    draw_stroke.register()
    export_data.register()

And I’ve given the following files their own register/unregister classes:

def register():    
    bpy.utils.register_module(__name__)


def unregister():
    bpy.utils.unregister_module(__name__)
    
if __name__ == "__main__": 
    register()

But when I load the plugin into blender, it gives me this error:


They all have classes derived from the Blender API, so I don’t know whats up :confused:

I don’t think you need that if statement the init.py is going to call those register statements directly. But I have never seen that classes error before so I am not sure what to think…

I removed the if statement, but it didn’t change anything. As soon as this error occurs if I try to load the plugin again it just says that it fails, with no specific reason as to why it failed loading the module. I have to restart Blender at that point to check for the error again. Damn -.-

I use

if "bpy" in locals():
    import imp
    imp.reload(mesh_bump)
    imp.reload(face_inset_fillet)
    imp.reload(mesh_bevel_witold)
    imp.reload(mesh_filletplus)
    imp.reload(mesh_normal_smooth)
    imp.reload(mesh_polyredux)
    imp.reload(mesh_vertex_chamfer)
    imp.reload(mesh_mextrude_plus)


else:
    from . import mesh_bump
    from . import face_inset_fillet
    from . import mesh_bevel_witold
    from . import mesh_filletplus
    from . import mesh_normal_smooth
    from . import mesh_polyredux
    from . import mesh_vertex_chamfer
    from . import mesh_mextrude_plus


import bpy

then

# Define "Extras" menu
def menu_func(self, context):
    self.layout.menu('VIEW3D_MT_edit_mesh_extras', icon='PLUGIN')




def register():
    bpy.utils.register_module(__name__)


    # Add "Extras" menu to the "Add Mesh" menu
    bpy.types.VIEW3D_MT_edit_mesh_specials.prepend(menu_func)




def unregister():
    bpy.utils.unregister_module(__name__)


    # Remove "Extras" menu from the "Add Mesh" menu.
    bpy.types.VIEW3D_MT_edit_mesh_specials.remove(menu_func)


if __name__ == "__main__":
    register()

although this works too:

# register the class
def register():
    bpy.utils.register_module(__name__)


    pass


def unregister():
    bpy.utils.unregister_module(__name__)


    pass


if __name__ == "__main__":
    register()

Thanks for the suggestion, i tried using this in my code but the same issue occurs. :C

It is good idea to switch over to importlib instead of imp if you are writing new code for the blender, since the switch to python 3.4 with 2.71
Deprecated since version 3.4: The imp package is pending deprecation in favor of importlib.
https://docs.python.org/3.4/library/importlib.html?highlight=importlib#module-importlib
https://docs.python.org/3.4/library/imp.html?highlight=imp#module-imp

Oh, thanks for the tip. I tried implementing it (at least I think I did, i feel like such a python noob atm >_>):

import bpy, importlib

if "bpy" in locals():
    importlib.reload(update)
    importlib.reload(properties)
    importlib.reload(user_interface)
    importlib.reload(events)
    importlib.reload(utilities)
    importlib.reload(draw_stroke)
    importlib.reload(export_data)




else:
    importlib.importmodule("update")
    importlib.importmodule("properties")
    importlib.importmodule("user_interface")
    importlib.importmodule("events")
    importlib.importmodule("utilities")
    importlib.importmodule("draw_stroke")
    importlib.importmodule("export_data")

It just comes up with this error:


I’ve also now tried clearing all of Blender’s files stored in the Application Library folder (on Mac OS X) to ensure nothing was conflicting with the plugin by accident, and it hasn’t changed the issue.

hi, If you pm me your files I’ll fix if you want.

I’ll link the files here (Download). I tried using your suggestion too, but it also didn’t solve the initial bug i encountered, and the imp package is pending deprecation which is why i tried using Linusy’s code.

Well, you can’t import bpy before testing if it is in locals(), that will always be true.

I had look deeper look, for some reason bpy.utils.register_module doesn’t work used like this. However if you use bpy.utils.register_class it does work, but then you need to make list of classes to register, silly but the editor does for me so… It would be nice to know why register_module fails in this scenario, maybe because the name is package.file ?


import importlib


modules =  [ "properties", "user_interface", 
             "draw_stroke", "export_data", "events"]


imported_modules = []


if "bpy" in locals():
    for im in imported_modules:
        importlib.reload(im)
else:     
    for m in modules:
        im =  importlib.import_module(".{}".format(m), __package__)
        imported_modules.append(im)
import bpy


def register():
    for im in imported_modules:
        if hasattr(im, 'register'):
            im.register()


def unregister():
    for im in imported_modules:
        if hasattr(im, 'unregister'):
            im.unregister()

I removed some import * also.
The addon might not work as this, but it does register. :slight_smile:

Ahh, whoops e_e. I changed it so boy is imported below the reload check, and I get another error:


Oh awesome Linusy, thanks! It does seem strange that it wouldn’t work, I honestly have no idea why it wouldn’t. This is extremely helpful though, I can finally start adding more features :smiley:

It’s import_module with underscore, but have look at what I did above.

Yeah, I see what you’ve done. I was honestly hoping id be able to avoid needing to import classes individually, but at least it works. The UI is broken, but it likely just needs some loose ends tied up from moving to a multi-file setup. tysm! :smiley:

I hope somebody can shed a light on why register_module doesn’t work used like this, that class thing is really ugly… I have no clue right now.
I meant more that you should look at init.py

In terms of finding the root cause or the code you edited? I did check out and it now makes sense, ^^. The only other thing I’m currently having trouble with is refreshing the script inside Blender, as I edit it outside, as it doesn’t seem to respond at all, but it’s probably something stupid i’m doing with the setup e_e.

Glad I could help.
F8 reload? I don’t use that as much as I should…

I tried that and it doesn’t reload unless i unload and reload the package from the add-on menu, :confused:

Hmm, the was a mistake in properties.py unregister function,
It was




def unregister():
    for cls in objectClasses + sceneClasses:
        bpy.utils.register_class(cls)

it should be


def unregister():
    for cls in objectClasses + sceneClasses:
        bpy.utils.unregister_class(cls)