5
$\begingroup$

How would I approach editing/reloading/printing to console a multi-file addon?

There's a (someone else's) multi-file addon with a complex structure of libraries and classes with a bug in a particular class and I'd like to try to fix it. However I never made an addon with several separated files and I don't understand how to reload it properly. At the moment I edit the original .py file and restart Blender which seems like a terrible workflow.

$\endgroup$
8
  • $\begingroup$ I really am not sure but you can try to reload the module with the 'importlib' module. Use "import importlib" and then "importlib.reload(module)" $\endgroup$
    – Gorgious
    Commented Mar 4, 2020 at 10:00
  • 2
    $\begingroup$ Does F3 > Reload Scripts help? You can also bind the operator to any hotkey... $\endgroup$
    – brockmann
    Commented Mar 4, 2020 at 10:57
  • $\begingroup$ @Gorgious I'll try that, thank you $\endgroup$ Commented Mar 4, 2020 at 11:50
  • $\begingroup$ @brockmann this doesn't seem to update the code $\endgroup$ Commented Mar 4, 2020 at 11:51
  • $\begingroup$ Does it for me. Is the addon enabled? Also see: blender.stackexchange.com/a/28505/31447 $\endgroup$
    – brockmann
    Commented Mar 4, 2020 at 12:04

2 Answers 2

2
$\begingroup$

Not sure why Reload Scripts didn't work at first for me, maybe I didn't save the correct file or whatnot, anyway, that's a possible way. My issues were however with this method is that

  • I have to edit files in Blender addons folder, not in my project folder;
  • this reloads all the scripts, on my machine with a lot of addons installed it takes about four seconds and plus after that some addons reset their settings;

In the end I followed this article for creating and debugging multifile addons. Author suggests using a specific setup for the __init__.py file and then provides a Blender script in the second part of the article to update only a specific addon. I've assigned this script to a hotkey and it works much faster than reloading all the scripts.

Here are the final scripts from the article. __init__.py:

bl_info = {
    'name': 'Test Multifile Addon',
    'category': 'All',
    'version': (0, 0, 1),
    'blender': (2, 80, 0)
}

modulesNames = ['addCube', 'addCubePanel'] # a list of imported module names

import sys
import importlib

modulesFullNames = {}
for currentModuleName in modulesNames:
    if 'DEBUG_MODE' in sys.argv:
        modulesFullNames[currentModuleName] = ('{}'.format(currentModuleName))
    else:
        modulesFullNames[currentModuleName] = ('{}.{}'.format(__name__, currentModuleName))

for currentModuleFullName in modulesFullNames.values():
    if currentModuleFullName in sys.modules:
        importlib.reload(sys.modules[currentModuleFullName])
    else:
        globals()[currentModuleFullName] = importlib.import_module(currentModuleFullName)
        setattr(globals()[currentModuleFullName], 'modulesNames', modulesFullNames)

def register():
    for currentModuleName in modulesFullNames.values():
        if currentModuleName in sys.modules:
            if hasattr(sys.modules[currentModuleName], 'register'):
                sys.modules[currentModuleName].register()

def unregister():
    for currentModuleName in modulesFullNames.values():
        if currentModuleName in sys.modules:
            if hasattr(sys.modules[currentModuleName], 'unregister'):
                sys.modules[currentModuleName].unregister()

if __name__ == "__main__":
    register()

Blender script to reload the addon:

import os
import sys

filesDir = "d:/Python/TestMultifile"

initFile = "__init__.py"

if filesDir not in sys.path:
    sys.path.append(filesDir)

file = os.path.join(filesDir, initFile)

if 'DEBUG_MODE' not in sys.argv:
    sys.argv.append('DEBUG_MODE')

exec(compile(open(file).read(), initFile, 'exec'))

if 'DEBUG_MODE' in sys.argv:
    sys.argv.remove('DEBUG_MODE')
$\endgroup$
1
0
$\begingroup$

I have found an easiest method. In this example the addon has two modules - main and operator

__init__.py:

bl_info = {
    # Your regular bl_info goes here.
}

# This is essential part
if "bpy" in locals():
    import importlib
    importlib.reload(main)
    importlib.reload(operator)
else:
    import bpy
    from . import main
    from . import operator

classes = (
    main.AddonProperties,
    operator.OBJECT_OT_skt_my_operator,
)

def register():
    for cls in classes:
        bpy.utils.register_class(cls)
$\endgroup$
1
  • 1
    $\begingroup$ To keep code "modular" I would recommend each module with classes to register having a register method as above.. eg modules = [main, operator] then in the register method loop the modules and mod.register() IMO making adding another module easier to test and integrate into addon. $\endgroup$
    – batFINGER
    Commented Jun 9, 2020 at 1:42

You must log in to answer this question.

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