Changeset View
Changeset View
Standalone View
Standalone View
release/scripts/modules/bpy/utils/module_loader.py
- This file was added.
| """ModuleLoader class provides an interface for addons to load modules | |||||
| and register and unregister classes""" | |||||
| import bpy | |||||
| import inspect | |||||
| import importlib | |||||
| import sys | |||||
| class ModuleLoader: | |||||
| """Loads modules and registers/unregisters classes""" | |||||
| def __init__(self, main_module_name): | |||||
| """Create a Module Loader. | |||||
| typical usage: | |||||
| loader = ModuleLoader(__name__) | |||||
| loader.import_modules(modules, | |||||
| package = __package__, reload = "bpy" in locals()) | |||||
| """ | |||||
| self.loaded_modules = [] # list of loaded modules | |||||
| main_module = sys.modules[main_module_name] | |||||
| self.main_module = LoadedModule(main_module, main = True) | |||||
| def import_modules(self, module_names, package, reload = False): | |||||
| for name in module_names: | |||||
| #print(("reloading " if reload else "loading ") + module) | |||||
| # import_module doesn't reload if already loaded, but we need a | |||||
| # reference to the module to reload. | |||||
| mod = importlib.import_module("." + name, package) | |||||
| self.add_module(mod) | |||||
| if reload: | |||||
| importlib.reload(mod) | |||||
| def add_module(self, module): | |||||
| self.loaded_modules.append(LoadedModule(module)) | |||||
| def register(self): | |||||
| """Registers classes for the main module and all loaded submodules. | |||||
| Calls register() on submodules if they have defined it. | |||||
| """ | |||||
| self.register_classes() | |||||
| for lmod in self._all_modules(): | |||||
| if not lmod.main and hasattr(lmod.module, "register"): | |||||
| lmod.module.register() | |||||
| def unregister(self): | |||||
| """Unregisters classes for the main module and all loaded submodules. | |||||
| Calls unregister() on submodules if they have defined it. | |||||
| """ | |||||
| for lmod in reversed(self._all_modules()): | |||||
| if not lmod.main and hasattr(lmod.module, "unregister"): | |||||
| lmod.module.unregister() | |||||
| self.unregister_classes() | |||||
| def register_classes(self): | |||||
| for lmod in self._all_modules(): | |||||
| lmod.register_classes() | |||||
| def unregister_classes(self): | |||||
| for lmod in reversed(self._all_modules()): | |||||
| lmod.unregister_classes() | |||||
| def _all_modules(self): | |||||
| return [self.main_module] + self.loaded_modules | |||||
| class LoadedModule: | |||||
| """Represents a loaded module""" | |||||
| _REGISTERABLE_CLASSES = ( | |||||
| bpy.types.Header, | |||||
| bpy.types.Menu, | |||||
| bpy.types.Operator, | |||||
| bpy.types.Macro, | |||||
| bpy.types.Panel, | |||||
| bpy.types.PropertyGroup, | |||||
| bpy.types.RenderEngine | |||||
| ) | |||||
| def __init__(self, module, main = False): | |||||
| self.module = module | |||||
| self.classes = [] # list of classes defined by this module | |||||
| self.main = main | |||||
| def register_classes(self): | |||||
| #print(f"processing module {self.module}") | |||||
| for obj in self._registerable_classes(): | |||||
| #print(f"registering {obj}") | |||||
| bpy.utils.register_class(obj) | |||||
| def unregister_classes(self): | |||||
| for obj in reversed(list(self._registerable_classes())): | |||||
| #print(f"unregistering {obj}") | |||||
| bpy.utils.unregister_class(obj) | |||||
| # iterate all registerable classes in the given module. | |||||
| # sorts classes by the order that they were defined to handle | |||||
| # dependencies between classes. | |||||
| def _registerable_classes(self): | |||||
| if not self.classes: | |||||
| # cache list of classes | |||||
| self.classes = self._find_registerable_classes() | |||||
| return self.classes | |||||
| def _find_registerable_classes(self): | |||||
| classes = [] | |||||
| for name, obj in inspect.getmembers(self.module, inspect.isclass): | |||||
| if obj.__module__ == self.module.__name__: | |||||
| for rclass in LoadedModule._REGISTERABLE_CLASSES: | |||||
| if issubclass(obj, rclass) and obj != rclass: | |||||
| #print(f"registerable class: {name}, class: {obj}") | |||||
| source, start_line = inspect.getsourcelines(obj) | |||||
| classes.append([obj, start_line]) | |||||
| def _line_order(value): | |||||
| return value[1] | |||||
| classes.sort(key = _line_order) | |||||
| return [i[0] for i in classes] | |||||
| def __repr__(self): | |||||
| return f"LoadedModule({self.module})" | |||||