2023-08-16 00:20:26 +10:00
|
|
|
# SPDX-FileCopyrightText: 2009-2023 Blender Authors
|
2023-06-15 13:09:04 +10:00
|
|
|
#
|
2022-02-11 09:07:11 +11:00
|
|
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
2009-10-31 20:16:59 +00:00
|
|
|
|
2009-07-19 13:32:02 +00:00
|
|
|
# for slightly faster access
|
2020-10-08 17:55:17 +11:00
|
|
|
from _bpy import ops as _ops_module
|
2009-11-13 09:28:05 +00:00
|
|
|
|
2020-10-08 17:55:17 +11:00
|
|
|
# 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
|
2009-07-17 12:26:40 +00:00
|
|
|
|
2020-10-08 17:55:17 +11:00
|
|
|
_ModuleType = type(_ops_module)
|
2010-02-22 23:32:58 +00:00
|
|
|
|
2009-10-31 01:23:49 +00:00
|
|
|
|
2020-10-08 17:43:08 +11:00
|
|
|
# -----------------------------------------------------------------------------
|
|
|
|
# Callable Operator Wrapper
|
2009-07-17 12:26:40 +00:00
|
|
|
|
2020-10-08 17:55:17 +11:00
|
|
|
class _BPyOpsSubModOp:
|
2012-07-03 09:02:41 +00:00
|
|
|
"""
|
2009-10-31 01:23:49 +00:00
|
|
|
Utility class to fake submodule operators.
|
|
|
|
|
|
|
|
eg. bpy.ops.object.somefunc
|
2012-07-03 09:02:41 +00:00
|
|
|
"""
|
2009-10-31 01:23:49 +00:00
|
|
|
|
2014-02-19 13:41:41 +11:00
|
|
|
__slots__ = ("_module", "_func")
|
2009-11-07 22:07:46 +00:00
|
|
|
|
2009-11-06 22:42:15 +00:00
|
|
|
def _get_doc(self):
|
2018-09-13 19:59:15 +10:00
|
|
|
idname = self.idname()
|
2020-10-08 17:55:17 +11:00
|
|
|
sig = _op_as_string(self.idname())
|
2018-09-13 19:59:15 +10:00
|
|
|
# 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
|
2023-02-12 14:37:16 +11:00
|
|
|
# native ops and py ones! See #39158.
|
2018-09-13 19:59:15 +10:00
|
|
|
# op_class = getattr(bpy.types, idname)
|
2020-10-08 17:55:17 +11:00
|
|
|
op_class = _op_get_rna_type(idname)
|
2018-09-13 19:59:15 +10:00
|
|
|
descr = op_class.description
|
2024-04-27 16:06:51 +10:00
|
|
|
return "{:s}\n{:s}".format(sig, descr)
|
2009-11-07 22:07:46 +00:00
|
|
|
|
2010-11-04 12:59:03 +00:00
|
|
|
@staticmethod
|
|
|
|
def _parse_args(args):
|
|
|
|
C_exec = 'EXEC_DEFAULT'
|
2012-06-27 21:41:17 +00:00
|
|
|
C_undo = False
|
|
|
|
|
2023-05-23 14:34:09 +10:00
|
|
|
is_exec = is_undo = False
|
2012-06-27 21:41:17 +00:00
|
|
|
|
2019-05-09 13:11:36 +10:00
|
|
|
for arg in args:
|
2023-05-23 14:34:09 +10:00
|
|
|
if is_exec is False and isinstance(arg, str):
|
2012-06-27 21:41:17 +00:00
|
|
|
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
|
2010-11-04 12:59:03 +00:00
|
|
|
else:
|
2023-05-23 14:34:09 +10:00
|
|
|
raise ValueError("1-2 args execution context is supported")
|
2010-11-04 12:59:03 +00:00
|
|
|
|
2023-05-23 14:34:09 +10:00
|
|
|
return C_exec, C_undo
|
2010-11-04 12:59:03 +00:00
|
|
|
|
2010-11-22 17:21:18 +00:00
|
|
|
@staticmethod
|
2018-04-20 09:56:55 +02:00
|
|
|
def _view_layer_update(context):
|
|
|
|
view_layer = context.view_layer
|
|
|
|
if view_layer: # None in background mode
|
|
|
|
view_layer.update()
|
2010-11-22 17:21:18 +00:00
|
|
|
else:
|
2010-11-22 23:41:00 +00:00
|
|
|
import bpy
|
2010-11-22 17:21:18 +00:00
|
|
|
for scene in bpy.data.scenes:
|
2019-06-04 14:36:53 +02:00
|
|
|
for view_layer in scene.view_layers:
|
|
|
|
view_layer.update()
|
2010-11-22 17:21:18 +00:00
|
|
|
|
2009-11-06 22:42:15 +00:00
|
|
|
__doc__ = property(_get_doc)
|
2009-10-31 01:23:49 +00:00
|
|
|
|
|
|
|
def __init__(self, module, func):
|
2014-02-19 13:41:41 +11:00
|
|
|
self._module = module
|
|
|
|
self._func = func
|
2009-10-31 01:23:49 +00:00
|
|
|
|
2010-11-04 12:59:03 +00:00
|
|
|
def poll(self, *args):
|
2023-05-23 14:34:09 +10:00
|
|
|
C_exec, _C_undo = _BPyOpsSubModOp._parse_args(args)
|
|
|
|
return _op_poll(self.idname_py(), C_exec)
|
2010-09-01 11:16:11 +00:00
|
|
|
|
2009-10-31 01:23:49 +00:00
|
|
|
def idname(self):
|
2023-09-08 16:58:00 +10:00
|
|
|
# `submod.foo` -> `SUBMOD_OT_foo`.
|
2014-02-19 13:41:41 +11:00
|
|
|
return self._module.upper() + "_OT_" + self._func
|
2010-01-31 14:46:28 +00:00
|
|
|
|
2010-01-26 08:41:16 +00:00
|
|
|
def idname_py(self):
|
2014-02-19 13:41:41 +11:00
|
|
|
return self._module + "." + self._func
|
2009-10-31 01:23:49 +00:00
|
|
|
|
|
|
|
def __call__(self, *args, **kw):
|
2010-11-18 16:33:13 +00:00
|
|
|
import bpy
|
|
|
|
context = bpy.context
|
2009-10-31 01:23:49 +00:00
|
|
|
|
|
|
|
# Get the operator from blender
|
2010-11-18 16:33:13 +00:00
|
|
|
wm = context.window_manager
|
|
|
|
|
2019-08-11 22:41:04 +10:00
|
|
|
# Run to account for any RNA values the user changes.
|
|
|
|
# NOTE: We only update active view-layer, since that's what
|
2018-04-20 09:56:55 +02:00
|
|
|
# operators are supposed to operate on. There might be some
|
|
|
|
# corner cases when operator need a full scene update though.
|
2020-10-08 17:55:17 +11:00
|
|
|
_BPyOpsSubModOp._view_layer_update(context)
|
2010-11-22 17:21:18 +00:00
|
|
|
|
2009-10-31 01:23:49 +00:00
|
|
|
if args:
|
2023-05-23 14:34:09 +10:00
|
|
|
C_exec, C_undo = _BPyOpsSubModOp._parse_args(args)
|
|
|
|
ret = _op_call(self.idname_py(), kw, C_exec, C_undo)
|
2009-10-31 01:23:49 +00:00
|
|
|
else:
|
2023-05-23 14:34:09 +10:00
|
|
|
ret = _op_call(self.idname_py(), kw)
|
2010-01-26 08:41:16 +00:00
|
|
|
|
2010-11-18 16:33:13 +00:00
|
|
|
if 'FINISHED' in ret and context.window_manager == wm:
|
2020-10-08 17:55:17 +11:00
|
|
|
_BPyOpsSubModOp._view_layer_update(context)
|
2010-01-26 08:41:16 +00:00
|
|
|
|
|
|
|
return ret
|
2009-10-31 01:23:49 +00:00
|
|
|
|
2018-09-13 18:16:06 +10:00
|
|
|
def get_rna_type(self):
|
|
|
|
"""Internal function for introspection"""
|
2020-10-08 17:55:17 +11:00
|
|
|
return _op_get_rna_type(self.idname())
|
2018-09-13 18:16:06 +10:00
|
|
|
|
2020-09-01 17:02:51 +10:00
|
|
|
@property
|
|
|
|
def bl_options(self):
|
2020-10-08 17:55:17 +11:00
|
|
|
return _op_get_bl_options(self.idname())
|
2020-09-01 17:02:51 +10:00
|
|
|
|
2010-09-07 15:17:42 +00:00
|
|
|
def __repr__(self): # useful display, repr(op)
|
2020-10-08 17:55:17 +11:00
|
|
|
return _op_as_string(self.idname())
|
2009-10-31 01:23:49 +00:00
|
|
|
|
2010-09-07 15:17:42 +00:00
|
|
|
def __str__(self): # used for print(...)
|
2024-04-27 16:06:51 +10:00
|
|
|
return (
|
|
|
|
"<function bpy.ops.{:s}.{:s} at 0x{:x}'>".format(
|
|
|
|
self._module, self._func, id(self),
|
|
|
|
)
|
|
|
|
)
|
2009-07-17 12:26:40 +00:00
|
|
|
|
2018-07-03 06:27:53 +02:00
|
|
|
|
2020-10-08 17:43:08 +11:00
|
|
|
# -----------------------------------------------------------------------------
|
|
|
|
# Sub-Module Access
|
|
|
|
|
|
|
|
def _bpy_ops_submodule__getattr__(module, func):
|
|
|
|
# Return a value from `bpy.ops.{module}.{func}`
|
|
|
|
if func.startswith("__"):
|
|
|
|
raise AttributeError(func)
|
2020-10-08 17:55:17 +11:00
|
|
|
return _BPyOpsSubModOp(module, func)
|
2020-10-08 17:43:08 +11:00
|
|
|
|
|
|
|
|
|
|
|
def _bpy_ops_submodule__dir__(module):
|
|
|
|
functions = set()
|
|
|
|
module_upper = module.upper()
|
|
|
|
|
2020-10-08 17:55:17 +11:00
|
|
|
for id_name in _op_dir():
|
2020-10-08 17:43:08 +11:00
|
|
|
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()
|
2020-10-08 17:55:17 +11:00
|
|
|
for id_name in _op_dir():
|
2020-10-08 17:43:08 +11:00
|
|
|
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)
|