I’m wondering if anyone has a good workflow for addon development in blender 2.91. As far as I can tell, blender (on OSX) doesn’t support F8 to reload addons. Addons that are reloaded manually also do not recompile unless init.py changes on disk, which makes multi-file addons very tedious to edit/reload.
e.g.
change code in ExampleClass.py
“save” init.py (no edits are necessary, but it must write to disk)
toggle ExampleAddon in addon manager OR execute “bpy.ops.preferences.addon_enable(module=‘example_addon’)” in the console
Edit: found out that F8 is now F3 > Reload Scripts but would love to know how to assign this to a hotkey. Atleast it doesn’t suffer from having to save init.py to disk constantly
If any further improvements are possible to an iterative dev workflow of an addon, please share!
Edit: I’m very grateful for all the useful feedback shared on this thread.
I just posted this WIP addon, to help speed up addon development by quickening reloading.
That post is kindof funny but extremely helpful. The only plot twist for me is that running “bpy.ops.script.reload()” crashes blender after the second or third time.
I usually have this after imports in my __init__.py
if locals().get('loaded'):
loaded = False
from importlib import reload
from sys import modules
modules[__name__] = reload(modules[__name__])
for name, module in modules.items():
if name.startswith(f"{__package__}."):
globals()[name] = reload(module)
del reload, modules
And at the bottom of the file:
loaded = True
It’s not order-aware reload, which I think is fine because it lets me resolve circular dependency issues immediately as they arise instead of the usual house of cards setup.
I then use this operator to reload a single module, which can be hotkeyed:
class SCRIPT_OT_reload_addon(Operator):
bl_idname = "script.reload_addon_module"
bl_label = "Reload Addon Module"
module: bpy.props.StringProperty(default="")
def execute(self, context):
import addon_utils
default, state = addon_utils.check(self.module)
if default and state:
modules = bpy.utils._sys.modules
m = modules.get(self.module)
if m is not None:
from importlib import reload
m.unregister()
m = modules[self.module] = reload(m)
print("reloading", self.module)
for n, sub in modules.items():
if n.startswith("%s." % self.module):
modules[n] = reload(sub)
print("reload submodule", n)
m.register()
return {'CANCELLED'}
It’s a much more direct reload of a single addon.
Other than that, not sure what there is for osx, but I use ConEmu terminal emulator with a macro set to ctrl + 1 which kills and restarts blender. For those times when I really just want to hit a reset switch.
I just found that the reload scripts operator is hidden here, so the shortcut can be assigned once again manually.
However what I did for many days that worked nicely was to hit refresh and check-uncheck. But this was a workaround because I thought that developers removed the reload mechanism.
That looks very helpful. I was thinking of making a module that “watches” the addon folder for changes and just runs bpy.ops.script.reload() (on focus change, ofcourse) but that might be overkill.
If you develop with VSCode, you should try the extension https://github.com/JacquesLucke/blender_vscode. It offers addon reloading on file change (check its setting, not enabled by default). With this I almost never reload manually which is a huge time saver.
I’m not sure how relatable this is to blender, but I have quite allot of experience developing for other DCCs and have taken some time to try and streamline this process
Separate API / UI
Firstly this is simply good practice, Secondly the objects registered with blender will in turn reference the modules of your standalone API, which are trivially reloaded.
Use small simple PURE functions
In computer programming, a pure function is a function that has the following properties: Its return value is the same for the same arguments. Its evaluation has no side effects. Thus a pure function is a computational analogue of a mathematical function.
Try to write your API so its functions do only one simple thing, and have no side effects. This allows you to easily test your code outside of the addon and hugely increases your iteration speed.
keep things simple.
I see people trying to make everything a class these days and it tends to lead to very rigid code which doesn’t scale well ( nothing against classes but the can be tricky to use right )
If it’s of any use, I got tired of that annoying routine (disable->remove->install->enable) and bundled it into a single button.
I’ve been using it only with single file scripts. I’m sure there’s a clever way to turn it into a more sentient reload-butler-robot, but it’s not in my immediate plans to do so.
This is good advice, especially relevant with how blender registers operators, though it is kindof funny that my little suite of tools could cleanly span 5 seperate addons. Maybe down the road.
One remaining addon-dev-workflow question is regarding version control.
My current approach is to just make a symlink from myAddon repo project folder, to /Users/me/Library/Application\ Support/Blender/2.91/scripts/addons/myAddon (OSX path)
It feels a bit hacky, but it seems to work alright (just ignore those __pycache__ files!).
Anyone have a different approach to this?
D\programming\blenderaddons: this directory is mostly a warehouse with all of the WIP, from time to time I pick one addon to work on rather than having all of them enabled at the same time.
D:\graphics\blenderaddons29 any third party script is installed here, when I need some of my own scripts installed here (from blenderaddons) I just place a symlink
P.S. Though one really great idea for Blender improvement here is that if File Paths > Scripts setting can hold multiple paths separated with ; it will completely remove any need for using directory junctions.