Issues Working with Multi-File Plugins

Thanks, but I did actually notice and fix that and its still has issues refreshing :confused:

Just in case anyone comes across this forum through a search, I thought i’d post the solution I found, as well as one Linusy messaged me:

My solution involved defining the modules i wanted to reload or register/unregister without the use of a list, as I found in a previous setup that as they became unregistered, they got removed from the list for some reason. This init file still has issues reloading when new definitions are introduced, and I’m unsure if it’s my fault or not :x.


    bl_info = {    
    "name": "Game Tools 0.60",
    "author": "Crocadillian/Takanu @ Polarised Games",
    "version": (0,60),
    "blender": (2, 7, 1),
    "api": 39347,
    "location": "3D View > Object Mode > Tools > Game Tools",
    "description": "Aids the development and export of game assets with various fancy tools.",
    "warning": "Beta",
    "wiki_url": "",
    "category": "Object"}
    
# Start importing all the addon files
# The init file just gets things started, no code needs to be placed here.
import importlib


modules =  [ "properties", "update", "operators", "definitions", "user_interface"]


imported_modules = []


if "bpy" in locals():
    print("*"*40)
    print("Reloading Modules")
    print("*"*40)


    importlib.reload(properties)
    importlib.reload(update)
    importlib.reload(operators)
    importlib.reload(definitions)
    importlib.reload(user_interface)


else:
    print("*"*40)     
    print("Importing Modules")
    print("*"*40)
    for m in modules:
        
        im =  importlib.import_module(".{}".format(m), __package__)
        print(im)
        imported_modules.append(im)
        
import bpy


def register():
    print("*"*40)
    print("Registering Modules")
    print("*"*40)
    
    properties.register()
    operators.register()
    user_interface.register()


def unregister():
    print("*"*40)
    print("Unregistering Modules")
    print("*"*40)
    
    properties.unregister()
    operators.unregister()
    user_interface.unregister()

For the register and unregister classes in each file that has a class, I had to also define the unregister classes separately from the register classes in one particular file to avoid another error:

Standard Register/Unregister:




classes = (UI_Freeze_List, UI_Visibility_List, UI_Component_List, GT_Tools, GT_New_Asset, GT_Object_Name, GT_Object_Stage, GT_Object_Type, GT_Export)


def register():
    print("-"*40)
    print("Registering UI")
    
    for cls in classes:
        bpy.utils.register_class(cls)
    
    #bpy.utils.register_module(__name__)


def unregister():
    print("-"*40)
    print("Unregistering UI")
    
    for cls in classes:
        bpy.utils.unregister_class(cls)
        
    #bpy.utils.unregister_module(__name__)

Other Register/Unregister Code:


classes = (GT_Blank_Report, GT_Name_Report, GT_Update_Origin, GT_Assign_Group, GT_Assign_Base, GT_Remove_From_Base, GT_Create_New_Asset, GT_Add_Components, GT_Find_Components, GT_Remove_Components, GT_Duplicate_Base, GT_Merge_Base, GT_Duplicate_Group, GT_Freeze, GT_Unfreeze, GT_Create_Group, GT_Create_Low_Poly_Base, GT_Create_Low_Poly_Component, GT_Create_Collision, GT_Create_Cage, GT_Group_Create_Collision, GT_Group_Create_Cage, GT_Check_Mesh, GT_Focus_Asset, GT_Export_Assets)


def register():
    for cls in classes:
        bpy.utils.register_class(cls)
        
    print("-"*40)
    print("Registering Operators")
    
    #bpy.utils.register_module(__name__)


def unregister():
    
    print("-"*40)
    print("Unregistering Operators")
    
    
    bpy.utils.unregister_class(GT_Blank_Report)
    bpy.utils.unregister_class(GT_Name_Report)
    bpy.utils.unregister_class(GT_Update_Origin)
    bpy.utils.unregister_class(GT_Assign_Group)
    bpy.utils.unregister_class(GT_Assign_Base)
    bpy.utils.unregister_class(GT_Remove_From_Base)
    bpy.utils.unregister_class(GT_Create_New_Asset)
    bpy.utils.unregister_class(GT_Add_Components)
    bpy.utils.unregister_class(GT_Find_Components)
    bpy.utils.unregister_class(GT_Remove_Components)
    bpy.utils.unregister_class(GT_Duplicate_Base)
    bpy.utils.unregister_class(GT_Merge_Base)
    bpy.utils.unregister_class(GT_Duplicate_Group)
    bpy.utils.unregister_class(GT_Freeze)
    bpy.utils.unregister_class(GT_Unfreeze)
    bpy.utils.unregister_class(GT_Create_Group)
    bpy.utils.unregister_class(GT_Create_Low_Poly_Base)
    bpy.utils.unregister_class(GT_Create_Low_Poly_Component)
    bpy.utils.unregister_class(GT_Create_Collision)
    bpy.utils.unregister_class(GT_Create_Cage)
    bpy.utils.unregister_class(GT_Group_Create_Collision)
    bpy.utils.unregister_class(GT_Group_Create_Cage)
    bpy.utils.unregister_class(GT_Check_Mesh)
    bpy.utils.unregister_class(GT_Focus_Asset)
    bpy.utils.unregister_class(GT_Export_Assets)
        
    #bpy.utils.unregister_module(__name__)

Linusy also provided a more elegant solution for the init file, which i’m currently using:


# Start importing all the addon files
# The init file just gets things started, no code needs to be placed here.
import importlib




modules =  [ "properties", "user_interface", "update",
             "definitions", "operators"]

imported_modules = []


for m in modules:
    im =  importlib.import_module(".{}".format(m), __name__)
    imported_modules.append(im)

if "bpy" in locals():
    for im in imported_modules:
        importlib.reload(im)


import bpy


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

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



Thanks for posting the solution. Here is one other thing that I add to my init, with the recent support for AddOn Preferences.

This code adds one boolean flag to the AddOn’s preferences section under the User Preferences window. You can define other properties too.


from bpy.types import Operator, AddonPreferences
def updateBlendshadePreferences(self,context):
    utility.SHOW_MESSAGES = self.show_debug
    #utility.USE_CUSTOM_COLOR = self.show_colors
    
class BlendshadePreferences(AddonPreferences):
    # This must match the addon name, use '__package__'
    # when defining this in a submodule of a python package.
    bl_idname = __name__


    show_debug = bpy.props.BoolProperty(name="Show Blendshade Debug Messages In The Console", description="When enabled debug messages will appear in the console window.", default=False, options={'ANIMATABLE'}, subtype='NONE', update=updateBlendshadePreferences)
    #show_colors = bpy.props.BoolProperty(name="Show Blendshade Node Colors", description="Not safe for everyday usage, uses threading. Do not use with unsaved file.", default=False, options={'ANIMATABLE'}, subtype='NONE', update=updateBlendshadePreferences)


    def draw(self, context):
        self.layout.prop(self, "show_debug")
        #self.layout.prop(self, "show_colors")


def register ()
    #
    # Other registers happen first.
    #

    # Make sure the intial preferences value gets set.
    bpy.utils.register_class(BlendshadePreferences)
    user_preferences = bpy.context.user_preferences
    addon_prefs = user_preferences.addons["blendshade"].preferences  #key/id is name of AddOn folder (is case sensitive and MUST match).
    utility.SHOW_MESSAGES = addon_prefs.show_debug


def unregister():
    bpy.utils.unregister_class(BlendshadePreferences)

In this case I have a global variable named “SHOW_MESSAGES” (defined inside utility.py) which gets over written by the preferences to determine if my debug line gets displayed in the console.

@Atom
I copied your preferences some time ago then moved them into a separate file recently. Very useful example.

Here I wanted to note on one thing, you use name for .bl_idname and then hard code in another place, wouldn’t it be more logical to use name in both locations? Like this.


def register()
    ...
    addon_prefs = user_preferences.addons[__name__].preferences  

You maybe right on that. When I first got preferences to work I was “hard coding” the name. I guess that is no longer needed. Moving them to another external file is probably the way to go as well.