Based on this link: https://wiki.blender.org/wiki/Reference/Release_Notes/2.80/Python_API/Addons#Registration
We have to to create list of classes for registering. But with big addons this is lot of repetitive work - manually creating list of 20 - 40 it time consuming, and there is easy way to make mistake.
Is there better, automated way to create class list? Some script that goes through all .py files in current addon directory and creates this list automatically?
have you investigated the link at the bottom?
If you have an addon with many classes, the list can be generated using this patch: Blender Archive - developer.blender.org
It seems this patch is not included in blender 2.8.
For Animation Nodes Iāll try to automate the registration process again. So far it seems to work well, but I did not test it much yet.
Also this is currently just a test implementation, maybe it helps anyway (scroll to the bottom): https://github.com/JacquesLucke/animation_nodes/blob/blender2.8/animation_nodes/init.py
Thanks @jacqueslucke. It worked with some small tweak.
In iter_register_dependencies() - this method will add āObjectā cls type to class list dependencies, if I use
PointerProperty( name='ObjPtr', type=bpy.types.Object)
In my addon.
But we do not want do add āObjectā class in register() function, so I just made sure we skip PointerProperties if
type.__name__ == 'Object'.
With additional check :
if value[0] in (bpy.props.PointerProperty, bpy.props.CollectionProperty) and value[1]['type'].__name__ != 'Object': #skip pointer properties if type = bpy.types.Object
I guess this I should check for PointerProperty it type is in [āMaterialā, āObjectā] or other type of data.
Thanks for the help.
Ah good point. I did not use these properties in AN yet for compability reasons but this should be fixed indeed, thank you
@Kia,
Does this mean it needs a custom build? Im not sure i understand the patch part
Hey again,
yesterday I wrote a new, cleaner version of this auto-load functionality.
I plan to use it in a template in my VS Code extension, however I need to do some more testing first. Maybe you want to test it as well.
You can download the file here: https://gist.github.com/JacquesLucke/11fecc6ea86ef36ea72f76ca547e795b
Just put it into your main addon directory.
Then in the __init__.py
file do this:
from . import auto_load
auto_load.init()
def register():
auto_load.register()
def unregister():
auto_load.unregister()
This should automatically
- import all modules in your addon
- discover all classes, that need to be registered
- sort the classes in case there are dependencies between them (e.g. when you use
bpy.props.CollectionProperty
- register all classes
- call a
register
function in all modules that have one (except in the main__init__.py
and inauto_load.py
)
Limitations:
- Canāt register keymaps, handlers, ⦠You have to do that manually in a
register
function in any module. Maybe Iāll provide utilities to register these things later as well. - You canāt have classes you want to register in the
__init__.py
file. However I think this is actually good.
Works fine for me. a
Could you make something like that with keymaps too?
I donāt think that something like that works well for keymaps. Because they canāt really be searched for that easily.
To make this work, we need some kind of additional container class or function that can be discovered automatically.
I mean, a code we could copy/paste like this one.
Thanks for this script!
It seems to work fine on my addon, just one thing:
In one file, I am doing this:
from bpy.types import OperatorFileListElement
...
class LUXCORE_OT_import_multiple_images(bpy.types.Operator, ImportHelper):
files: CollectionProperty(name="File Path", type=OperatorFileListElement)
And the auto_load script tries to register OperatorFileListElement
, probably because itās used in the CollectionProperty
, but fails.
I added a try/except and this is the exception:
Could not register class: <class 'bpy.types.OperatorFileListElement'>
register_class(...): already registered as a subclass
Iāll try to look into this later, but currently Iām more concerned with getting the rest of my addon to run
Ah thank you very much, this is exactly the kind of mistakes I was looking for here. Will fix it and upload a new version of the script.
I updated the script. Please check if it works now.
Yes it works.
That was fast!
By the way, you should also add "RenderEngine"
here: https://gist.github.com/JacquesLucke/11fecc6ea86ef36ea72f76ca547e795b#file-auto_load-py-L119
Done! Let me know if your find more errors like that
Interesting! When I was making complex addons for pre-2.8 blender, I came up with a somewhat different solution. I made an AddonManager class (a instance of which was shared between addonās modules) and used it to explicitly mark the classes I wanted to register/unregister. Something like this:
addon = AddonManager()
@addon.Panel
class OBJECT_PT_example_panel(bpy.types.Panel):
ā¦
and so on. (I didnāt use topological sorting, just removed Pointer/Collection properties before registration of all classes and added them back afterwards.)
This approach doesnāt require registrable classes to be in the module globals (e.g. addon decorators work just as well for classes generated inside some factory function), and can allow some handy conveniences ā for example, making an operator out of a function:
@addon.Operator(idname=āobject.reset_vectorsā, options={āREGISTERā, āUNDOā}, description=āreset all axesā)
def Operator_Reset_Vectors(self, context, event, vectors=āā):
ā¦
For an example of use in actual addon code, you can look e.g. here.
What do you folks think about it?
Reminds me of the way it was done in the old LuxBlend addon.
There was an Addon
class that kept track of classes that were marked with a decorator:
An addon manager like this is also a good idea but I donāt want to use it my addons because:
- Usually, when programming, I want to solve a problem and then not think about it anymore. With your approach it can still be quite easy to forget the decorator. I want that it just works.
- It is true that my approach only finds classes that are in
globals()
. However I think that is not necessarily bad. I also use factory functions sometimes but usually I need even more flexibility in these cases. E.g. when I want to create new operators while the addon is registered already. In these cases I want to be explicit about when classes are (un)registered. - I want to provide this
auto_load.py
as a template for others and I donāt want that it is used in many different files. That could make updating the template much harder later on. - Developing developer tools is easier when no decorators are needed. Because otherwise an insert-operator-template function would have to know about how this class will be registered. So it would have to be smart, which is usually bad and should be avoided.
For these reasons Iād probably go with an opposite approach. Instead of providing an auto_load.Panel
decorator, I create an auto_load.ignore
decorate that can be used for classes that should not be registered automatically.
Having said that, such decorators are still very useful. In Animation Nodes I also have decorators to create operators from functions and to register draw handlers. I just want to make the common case as simple as possible. Maybe Iāll still add some decorators to auto_load
. Could be a good way to handle registration of keymaps or so.
The idea of removing pointer and collection properties for registration is actually quite smart. I did not have this idea before. However I can also see some downsides (havenāt tested this approach yet, so maybe Iām wrong).
- It might not work well when you have base classes that have properties shared by multiple Operators (maybe it works but the base classes will be messed up afterwards).
- I donāt like the idea of removing stuff from the
__annotations__
dict of classes. Not sure if that should be done by anyone.
So Iāll probably keep using the toposort to solve dependencies, that way I donāt have to mess around with classes other people write.