# ##### BEGIN GPL LICENSE BLOCK ##### # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # ##### END GPL LICENSE BLOCK ##### # # for slightly faster access from _bpy import ops as _ops_module # op_add = _ops_module.add _op_dir = _ops_module.dir _op_poll = _ops_module.poll _op_call = _ops_module.call _op_as_string = _ops_module.as_string _op_get_rna_type = _ops_module.get_rna_type _op_get_bl_options = _ops_module.get_bl_options _ModuleType = type(_ops_module) # ----------------------------------------------------------------------------- # Callable Operator Wrapper class _BPyOpsSubModOp: """ Utility class to fake submodule operators. eg. bpy.ops.object.somefunc """ __slots__ = ("_module", "_func") def _get_doc(self): idname = self.idname() sig = _op_as_string(self.idname()) # XXX You never quite know what you get from bpy.types, # with operators... Operator and OperatorProperties # are shadowing each other, and not in the same way for # native ops and py ones! See T39158. # op_class = getattr(bpy.types, idname) op_class = _op_get_rna_type(idname) descr = op_class.description return "%s\n%s" % (sig, descr) @staticmethod def _parse_args(args): C_dict = None C_exec = 'EXEC_DEFAULT' C_undo = False is_dict = is_exec = is_undo = False for arg in args: if is_dict is False and isinstance(arg, dict): if is_exec is True or is_undo is True: raise ValueError("dict arg must come first") C_dict = arg is_dict = True elif is_exec is False and isinstance(arg, str): if is_undo is True: raise ValueError("string arg must come before the boolean") C_exec = arg is_exec = True elif is_undo is False and isinstance(arg, int): C_undo = arg is_undo = True else: raise ValueError("1-3 args execution context is supported") return C_dict, C_exec, C_undo @staticmethod def _view_layer_update(context): view_layer = context.view_layer if view_layer: # None in background mode view_layer.update() else: import bpy for scene in bpy.data.scenes: for view_layer in scene.view_layers: view_layer.update() __doc__ = property(_get_doc) def __init__(self, module, func): self._module = module self._func = func def poll(self, *args): C_dict, C_exec, _C_undo = _BPyOpsSubModOp._parse_args(args) return _op_poll(self.idname_py(), C_dict, C_exec) def idname(self): # submod.foo -> SUBMOD_OT_foo return self._module.upper() + "_OT_" + self._func def idname_py(self): # submod.foo -> SUBMOD_OT_foo return self._module + "." + self._func def __call__(self, *args, **kw): import bpy context = bpy.context # Get the operator from blender wm = context.window_manager # Run to account for any RNA values the user changes. # NOTE: We only update active view-layer, since that's what # operators are supposed to operate on. There might be some # corner cases when operator need a full scene update though. _BPyOpsSubModOp._view_layer_update(context) if args: C_dict, C_exec, C_undo = _BPyOpsSubModOp._parse_args(args) ret = _op_call(self.idname_py(), C_dict, kw, C_exec, C_undo) else: ret = _op_call(self.idname_py(), None, kw) if 'FINISHED' in ret and context.window_manager == wm: _BPyOpsSubModOp._view_layer_update(context) return ret def get_rna_type(self): """Internal function for introspection""" return _op_get_rna_type(self.idname()) @property def bl_options(self): return _op_get_bl_options(self.idname()) def __repr__(self): # useful display, repr(op) return _op_as_string(self.idname()) def __str__(self): # used for print(...) return ("" % (self._module, self._func, id(self))) # ----------------------------------------------------------------------------- # Sub-Module Access def _bpy_ops_submodule__getattr__(module, func): # Return a value from `bpy.ops.{module}.{func}` if func.startswith("__"): raise AttributeError(func) return _BPyOpsSubModOp(module, func) def _bpy_ops_submodule__dir__(module): functions = set() module_upper = module.upper() for id_name in _op_dir(): id_split = id_name.split("_OT_", 1) if len(id_split) == 2 and module_upper == id_split[0]: functions.add(id_split[1]) return list(functions) def _bpy_ops_submodule(module): result = _ModuleType("bpy.ops." + module) result.__getattr__ = lambda func: _bpy_ops_submodule__getattr__(module, func) result.__dir__ = lambda: _bpy_ops_submodule__dir__(module) return result # ----------------------------------------------------------------------------- # Module Access def __getattr__(module): # Return a value from `bpy.ops.{module}`. if module.startswith("__"): raise AttributeError(module) return _bpy_ops_submodule(module) def __dir__(): submodules = set() for id_name in _op_dir(): id_split = id_name.split("_OT_", 1) if len(id_split) == 2: submodules.add(id_split[0].lower()) else: submodules.add(id_split[0]) return list(submodules)