Module kawa_scripts.commons
Expand source code
# Kawashirov's Scripts (c) 2021 by Sergey V. Kawashirov
#
# Kawashirov's Scripts is licensed under a
# Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
#
# You should have received a copy of the license along with this
# work. If not, see <http://creativecommons.org/licenses/by-nc-sa/3.0/>.
#
#
from collections import deque as _deque
from time import perf_counter as _perf_counter
import contextlib as _contextlib
import bpy as _bpy
from bpy import context as _C
from mathutils import Vector as _Vector
from mathutils import Quaternion as _Quaternion
from mathutils import Matrix as _Matrix
from mathutils.geometry import area_tri as _area_tri
from ._internals import log as _log
import typing as _typing
if _typing.TYPE_CHECKING:
from typing import *
from bpy.types import *
class ConfigurationError(RuntimeError):
# Ошибка конфигурации
pass
class MaterialConfigurationError(ConfigurationError):
def __init__(self, mat, msg: str):
self.material = mat
msg = 'Material={0}: {1}'.format(mat, msg)
super().__init__(msg)
def poly2_area2(ps: 'Sequence[Vector]'):
# Площадь полигона, примерно, без учёта вогнутостей
length = len(ps)
if length < 3:
return 0
elif length == 3:
# Частый случай, оптимизация для треугольника
return _area_tri(ps[0], ps[1], ps[2])
elif length == 4:
# Частый случай, оптимизация для квада
return _area_tri(ps[0], ps[1], ps[2]) + _area_tri(ps[0], ps[2], ps[3])
else:
# Для пентагона и выше - Формула Гаусса
s = ps[length - 1].x * ps[0].y - ps[0].x * ps[length - 1].y
for i in range(length - 1):
s += ps[i].x * ps[i + 1].y
s -= ps[i + 1].x * ps[i].y
return 0.5 * abs(s)
def is_none_or_bool(value: 'Optional[bool]') -> 'bool':
return value is None or isinstance(value, bool)
def is_positive_int(pint: 'int') -> 'bool':
return isinstance(pint, int) and pint > 0
def is_positive_float(pfloat: 'float') -> 'bool':
return (isinstance(pfloat, int) or isinstance(pfloat, float)) and pfloat > 0
def is_none_or_positive_float(pfloat: 'float') -> 'bool':
return pfloat is None or ((isinstance(pfloat, int) or isinstance(pfloat, float)) and pfloat > 0)
def is_positive_or_zero_float(pfloat: 'float') -> 'bool':
return (isinstance(pfloat, int) or isinstance(pfloat, float)) and pfloat >= 0
def is_none_or_positive_or_zero_float(pfloat: 'float') -> 'bool':
return pfloat is None or ((isinstance(pfloat, int) or isinstance(pfloat, float)) and pfloat >= 0)
def is_valid_size_int(size: 'Tuple[int, int]') -> 'bool':
return isinstance(size, tuple) and len(size) == 2 and is_positive_int(size[0]) and is_positive_int(size[1])
def is_valid_size_float(size: 'Tuple[float, float]') -> 'bool':
return isinstance(size, tuple) and len(size) == 2 and is_positive_float(size[0]) and is_positive_float(size[1])
def is_valid_string(string: 'str') -> 'bool':
return isinstance(string, str) and len(string) > 0
def is_none_or_valid_string(string: 'str') -> 'bool':
return string is None or (isinstance(string, str) and len(string) > 0)
def identity_transform(obj: 'Object'):
obj.location = _Vector.Fill(3, 0.0)
obj.rotation_mode = 'QUATERNION'
obj.rotation_quaternion = _Quaternion()
obj.scale = _Vector.Fill(3, 1.0)
def copy_transform(from_obj: 'Object', to_obj: 'Object'):
to_obj.location = from_obj.location
to_obj.rotation_mode = from_obj.rotation_mode
to_obj.rotation_axis_angle = from_obj.rotation_axis_angle
to_obj.rotation_euler = from_obj.rotation_euler
to_obj.rotation_quaternion = from_obj.rotation_quaternion
to_obj.scale = from_obj.scale
def move_children_to_grandparent(obj: 'Object'):
for child in obj.children:
set_parent_keep_world(child, obj.parent)
def set_parent_keep_world(child: 'Object', parent: 'Object'):
m = child.matrix_world.copy()
child.parent = parent
child.parent_type = 'OBJECT'
child.matrix_parent_inverse = _Matrix.Identity(4)
child.matrix_world = m
def apply_parent_inverse_matrix(obj: 'Object'):
identity = _Matrix.Identity(4)
if obj.parent_type != 'OBJECT' or obj.matrix_parent_inverse == identity:
return False
mw = obj.matrix_world.copy()
obj.matrix_parent_inverse = identity
obj.parent_type = 'OBJECT'
obj.matrix_world = mw
return True
class KawaApplyParentInverseMatrices(_bpy.types.Operator):
bl_idname = "object.kawa_apply_parent_inverse_matrices"
bl_label = "Apply Parent Inverse Transform Matricies"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context: 'Context'):
if len(context.selected_objects) < 1:
return False # Должны быть выбраны какие-то объекты
if context.mode != 'OBJECT':
return False # Требуется режим OBJECT
return True
def execute(self, context: 'Context'):
applied = list(obj for obj in context.selected_objects if apply_parent_inverse_matrix(obj))
applied_strs = "".join("\n-\t{0}".format(repr(obj)) for obj in applied)
self.report({'INFO'}, "Applied {0} parent inverse matrices:{1}".format(len(applied), applied_strs))
return {'FINISHED'} if len(applied) > 0 else {'CANCELLED'}
def ensure_op_result(result: 'Iterable[str]', allowed_results: 'Iterable[str]', **kwargs):
if set(result) >= set(allowed_results):
raise RuntimeError('Operator has invalid result:', result, allowed_results, list(_C.selected_objects), kwargs)
def ensure_op_finished(result, **kwargs):
if 'FINISHED' not in result:
raise RuntimeError('Operator is not FINISHED: ', result, list(_C.selected_objects), kwargs)
def ensure_op_finished_or_cancelled(result, **kwargs):
if 'FINISHED' not in result and 'CANCELLED' not in result:
raise RuntimeError('Operator is not FINISHED: ', result, list(_C.selected_objects), kwargs)
def select_set_all(objects: 'Iterable[Object]', state: bool):
for obj in objects:
try:
obj.hide_set(False)
obj.select_set(state)
except Exception as exc:
_log.error(str(exc))
_log.error(repr(objects))
raise exc
def activate_object(obj: 'Object'):
obj.hide_set(False)
obj.select_set(True)
_C.view_layer.objects.active = obj
def activate_objects(objs: 'Iterable[Object]'):
for obj in objs:
activate_object(obj)
def ensure_deselect_all_objects():
# ensure_op_finished(bpy.ops.object.select_all(action='DESELECT'), name="bpy.ops.object.select_all(action='DESELECT')")
# Это быстрее, чем оператор, и позволяет отжать скрытые объекты
# _C.selected_objects выдаёт AttributeError: '_RestrictContext' object has no attribute 'selected_objects'
while len(_C.view_layer.objects.selected) > 0:
_C.view_layer.objects.selected[0].select_set(False)
class _TemporaryViewLayer(_contextlib.ContextDecorator):
# Does not work
def __init__(self, name=None):
self.name = None if name is None else str(name)
self.scene = None # type: Scene
self.temp_view_layer = None # type: ViewLayer
self.original_view_layer = None # type: ViewLayer
def __enter__(self):
self.scene = _C.scene
self.original_view_layer = _C.view_layer
name = '__Temporary'
if self.name:
name += '-' + self.name
self.temp_view_layer = self.scene.view_layers.new(name)
# _C.window.view_layer = self.temp_view_layer
_C.view_layer = self.temp_view_layer
return self
def __exit__(self, *exc):
try:
# _C.window.scene = self.scene
# _C.window.view_layer = self.original_view_layer
_C.scene = self.scene
_C.view_layer = self.original_view_layer
self.scene.view_layers.remove(self.temp_view_layer)
except ReferenceError:
pass # this is fine
class SaveSelection(_contextlib.ContextDecorator):
# TODO
def __init__(self, name=None):
self.last_active_object = None # type: Object
self.shown = None # type: List[Object]
self.selected = None # type: List[Object]
def __enter__(self):
self.last_active_object = _C.view_layer.objects.active
self.selected = list(_C.view_layer.objects.selected)
return self
# def hide_set(self, obj: 'Object', state: 'bool'):
# if self.hide_state is None:
# self.hide_state = dict()
# if obj not in self.hide_state.keys():
# self.hide_state[obj] = obj.hide_get()
# obj.hide_set(state)
#
# def select_set(self, obj: 'Object', state: 'bool'):
# if self.select_state is None:
# self.select_state = dict()
# if obj not in self.select_state.keys():
# self.select_state[obj] = obj.select_get()
# obj.select_set(state)
#
# def activate_object(self, obj: 'Object'):
# self.hide_set(obj, False)
# self.select_set(obj, True)
# _C.view_layer.objects.selected = obj
#
# def activate_objects(self, objs: 'Iterable[Object]'):
# for obj in objs:
# self.activate_object(obj)
def __exit__(self, *exc):
for obj in _C.view_layer.objects:
obj.select_set(obj in self.selected)
# if self.hide_state is not None:
# for obj, state in self.hide_state.items():
# try:
# obj.hide_set(state)
# except ReferenceError:
# pass # object invalid, this is fine
# if self.select_state is not None:
# for obj, state in self.select_state.items():
# try:
# obj.select_set(state)
# except ReferenceError:
# pass # object invalid, this is fine
try:
_C.view_layer.objects.active = self.last_active_object
except ReferenceError:
pass # object invalid, this is fine
def any_not_none(*args):
# Первый не-None, или None
for v in args:
if v is not None:
return v
return None
def get_mesh_safe(obj: 'Object') -> 'Mesh':
mesh = obj.data
if not isinstance(mesh, _bpy.types.Mesh):
raise ValueError("Object.data is not Mesh!", obj, mesh)
return mesh
def remove_all_geometry(obj: 'Object'):
import bmesh
# Очистка геометрии
bm = bmesh.new()
try:
mesh = get_mesh_safe(obj)
# Дегенеративные уебки, почему в Mesh нет API для удаления геометрии?
bm.from_mesh(mesh)
bm.clear() # TODO optimize?
bm.to_mesh(mesh)
finally:
bm.free()
def remove_all_vertex_colors(obj: 'Object'):
mesh = get_mesh_safe(obj)
while len(mesh.vertex_colors) > 0:
mesh.vertex_colors.remove(mesh.vertex_colors[0])
def remove_all_material_slots(obj: 'Object', slots=0):
while len(obj.material_slots) > slots:
_C.view_layer.objects.active = obj
ensure_op_finished(_bpy.ops.object.material_slot_remove(), name='bpy.ops.object.material_slot_remove')
def find_objects_with_material(material: 'Material', where: 'Iterable[Object]' = None) -> 'Set[Object]':
objects = set()
if where is None:
where = _C.scene.objects
for obj in where:
if not isinstance(obj.data, _bpy.types.Mesh):
continue
for slot in obj.material_slots:
if slot.material == material:
objects.add(obj)
return objects
def is_parent(parent_object: 'Object', child_object: 'Object') -> 'bool':
obj = child_object
while obj is not None:
if parent_object == obj:
return True
obj = obj.parent
return False
def find_all_child_objects(parent_object: 'Object', where: 'Optional[Container[Object]]' = None) -> 'Set[Object]':
child_objects = set()
deque = _deque() # type: Deque[Object]
deque.append(parent_object)
while len(deque) > 0:
child_obj = deque.pop()
deque.extend(child_obj.children)
if where is not None and child_obj not in where:
continue
child_objects.add(child_obj)
return child_objects
def merge_same_material_slots(obj: 'Object'):
# Объединяет слоты с одинаковыми материалами:
# Сначала объединяет индексы, затем удаляет освободившиеся слоты.
# Игнорирует пустые слоты
if len(obj.material_slots) < 2:
return
ensure_deselect_all_objects()
activate_object(obj)
mesh = get_mesh_safe(obj)
# Все материалы используемые на объекте
mats = set()
run_op = False
for slot in obj.material_slots:
if slot is None or slot.material is None:
continue
mats.add(slot.material)
for proc_mat in mats:
indices = list()
for slot in range(len(obj.material_slots)):
if obj.material_slots[slot].material is proc_mat:
indices.append(slot)
if len(indices) < 2:
continue
run_op = True
main_idx = indices[0]
for idx in indices[1:]:
for poly in mesh.polygons:
if poly.material_index == idx:
poly.material_index = main_idx
if run_op:
ensure_op_finished_or_cancelled(
_bpy.ops.object.material_slot_remove_unused(), name='bpy.ops.object.material_slot_remove_unused'
)
ensure_deselect_all_objects()
_K = _typing.TypeVar('_K')
_V = _typing.TypeVar('_V')
def dict_get_or_add(_dict: 'Dict[_K,_V]', _key: 'Optional[_K]', _creator: 'Callable[[],_V]') -> '_V':
value = _dict.get(_key)
if value is None:
value = _creator()
_dict[_key] = value
return value
classes = (
KawaApplyParentInverseMatrices,
)
Functions
def activate_object(obj: Object)
-
Expand source code
def activate_object(obj: 'Object'): obj.hide_set(False) obj.select_set(True) _C.view_layer.objects.active = obj
def activate_objects(objs: Iterable[Object])
-
Expand source code
def activate_objects(objs: 'Iterable[Object]'): for obj in objs: activate_object(obj)
def any_not_none(*args)
-
Expand source code
def any_not_none(*args): # Первый не-None, или None for v in args: if v is not None: return v return None
def apply_parent_inverse_matrix(obj: Object)
-
Expand source code
def apply_parent_inverse_matrix(obj: 'Object'): identity = _Matrix.Identity(4) if obj.parent_type != 'OBJECT' or obj.matrix_parent_inverse == identity: return False mw = obj.matrix_world.copy() obj.matrix_parent_inverse = identity obj.parent_type = 'OBJECT' obj.matrix_world = mw return True
def copy_transform(from_obj: Object, to_obj: Object)
-
Expand source code
def copy_transform(from_obj: 'Object', to_obj: 'Object'): to_obj.location = from_obj.location to_obj.rotation_mode = from_obj.rotation_mode to_obj.rotation_axis_angle = from_obj.rotation_axis_angle to_obj.rotation_euler = from_obj.rotation_euler to_obj.rotation_quaternion = from_obj.rotation_quaternion to_obj.scale = from_obj.scale
def dict_get_or_add(_dict: Dict[_K,_V], _key: Optional[_K], _creator: Callable[[],_V]) ‑> _V
-
Expand source code
def dict_get_or_add(_dict: 'Dict[_K,_V]', _key: 'Optional[_K]', _creator: 'Callable[[],_V]') -> '_V': value = _dict.get(_key) if value is None: value = _creator() _dict[_key] = value return value
def ensure_deselect_all_objects()
-
Expand source code
def ensure_deselect_all_objects(): # ensure_op_finished(bpy.ops.object.select_all(action='DESELECT'), name="bpy.ops.object.select_all(action='DESELECT')") # Это быстрее, чем оператор, и позволяет отжать скрытые объекты # _C.selected_objects выдаёт AttributeError: '_RestrictContext' object has no attribute 'selected_objects' while len(_C.view_layer.objects.selected) > 0: _C.view_layer.objects.selected[0].select_set(False)
def ensure_op_finished(result, **kwargs)
-
Expand source code
def ensure_op_finished(result, **kwargs): if 'FINISHED' not in result: raise RuntimeError('Operator is not FINISHED: ', result, list(_C.selected_objects), kwargs)
def ensure_op_finished_or_cancelled(result, **kwargs)
-
Expand source code
def ensure_op_finished_or_cancelled(result, **kwargs): if 'FINISHED' not in result and 'CANCELLED' not in result: raise RuntimeError('Operator is not FINISHED: ', result, list(_C.selected_objects), kwargs)
def ensure_op_result(result: Iterable[str], allowed_results: Iterable[str], **kwargs)
-
Expand source code
def ensure_op_result(result: 'Iterable[str]', allowed_results: 'Iterable[str]', **kwargs): if set(result) >= set(allowed_results): raise RuntimeError('Operator has invalid result:', result, allowed_results, list(_C.selected_objects), kwargs)
def find_all_child_objects(parent_object: Object, where: Optional[Container[Object]] = None) ‑> Set[Object]
-
Expand source code
def find_all_child_objects(parent_object: 'Object', where: 'Optional[Container[Object]]' = None) -> 'Set[Object]': child_objects = set() deque = _deque() # type: Deque[Object] deque.append(parent_object) while len(deque) > 0: child_obj = deque.pop() deque.extend(child_obj.children) if where is not None and child_obj not in where: continue child_objects.add(child_obj) return child_objects
def find_objects_with_material(material: Material, where: Iterable[Object] = None) ‑> Set[Object]
-
Expand source code
def find_objects_with_material(material: 'Material', where: 'Iterable[Object]' = None) -> 'Set[Object]': objects = set() if where is None: where = _C.scene.objects for obj in where: if not isinstance(obj.data, _bpy.types.Mesh): continue for slot in obj.material_slots: if slot.material == material: objects.add(obj) return objects
def get_mesh_safe(obj: Object) ‑> Mesh
-
Expand source code
def get_mesh_safe(obj: 'Object') -> 'Mesh': mesh = obj.data if not isinstance(mesh, _bpy.types.Mesh): raise ValueError("Object.data is not Mesh!", obj, mesh) return mesh
def identity_transform(obj: Object)
-
Expand source code
def identity_transform(obj: 'Object'): obj.location = _Vector.Fill(3, 0.0) obj.rotation_mode = 'QUATERNION' obj.rotation_quaternion = _Quaternion() obj.scale = _Vector.Fill(3, 1.0)
def is_none_or_bool(value: Optional[bool]) ‑> bool
-
Expand source code
def is_none_or_bool(value: 'Optional[bool]') -> 'bool': return value is None or isinstance(value, bool)
def is_none_or_positive_float(pfloat: float) ‑> bool
-
Expand source code
def is_none_or_positive_float(pfloat: 'float') -> 'bool': return pfloat is None or ((isinstance(pfloat, int) or isinstance(pfloat, float)) and pfloat > 0)
def is_none_or_positive_or_zero_float(pfloat: float) ‑> bool
-
Expand source code
def is_none_or_positive_or_zero_float(pfloat: 'float') -> 'bool': return pfloat is None or ((isinstance(pfloat, int) or isinstance(pfloat, float)) and pfloat >= 0)
def is_none_or_valid_string(string: str) ‑> bool
-
Expand source code
def is_none_or_valid_string(string: 'str') -> 'bool': return string is None or (isinstance(string, str) and len(string) > 0)
def is_parent(parent_object: Object, child_object: Object) ‑> bool
-
Expand source code
def is_parent(parent_object: 'Object', child_object: 'Object') -> 'bool': obj = child_object while obj is not None: if parent_object == obj: return True obj = obj.parent return False
def is_positive_float(pfloat: float) ‑> bool
-
Expand source code
def is_positive_float(pfloat: 'float') -> 'bool': return (isinstance(pfloat, int) or isinstance(pfloat, float)) and pfloat > 0
def is_positive_int(pint: int) ‑> bool
-
Expand source code
def is_positive_int(pint: 'int') -> 'bool': return isinstance(pint, int) and pint > 0
def is_positive_or_zero_float(pfloat: float) ‑> bool
-
Expand source code
def is_positive_or_zero_float(pfloat: 'float') -> 'bool': return (isinstance(pfloat, int) or isinstance(pfloat, float)) and pfloat >= 0
def is_valid_size_float(size: Tuple[float, float]) ‑> bool
-
Expand source code
def is_valid_size_float(size: 'Tuple[float, float]') -> 'bool': return isinstance(size, tuple) and len(size) == 2 and is_positive_float(size[0]) and is_positive_float(size[1])
def is_valid_size_int(size: Tuple[int, int]) ‑> bool
-
Expand source code
def is_valid_size_int(size: 'Tuple[int, int]') -> 'bool': return isinstance(size, tuple) and len(size) == 2 and is_positive_int(size[0]) and is_positive_int(size[1])
def is_valid_string(string: str) ‑> bool
-
Expand source code
def is_valid_string(string: 'str') -> 'bool': return isinstance(string, str) and len(string) > 0
def merge_same_material_slots(obj: Object)
-
Expand source code
def merge_same_material_slots(obj: 'Object'): # Объединяет слоты с одинаковыми материалами: # Сначала объединяет индексы, затем удаляет освободившиеся слоты. # Игнорирует пустые слоты if len(obj.material_slots) < 2: return ensure_deselect_all_objects() activate_object(obj) mesh = get_mesh_safe(obj) # Все материалы используемые на объекте mats = set() run_op = False for slot in obj.material_slots: if slot is None or slot.material is None: continue mats.add(slot.material) for proc_mat in mats: indices = list() for slot in range(len(obj.material_slots)): if obj.material_slots[slot].material is proc_mat: indices.append(slot) if len(indices) < 2: continue run_op = True main_idx = indices[0] for idx in indices[1:]: for poly in mesh.polygons: if poly.material_index == idx: poly.material_index = main_idx if run_op: ensure_op_finished_or_cancelled( _bpy.ops.object.material_slot_remove_unused(), name='bpy.ops.object.material_slot_remove_unused' ) ensure_deselect_all_objects()
def move_children_to_grandparent(obj: Object)
-
Expand source code
def move_children_to_grandparent(obj: 'Object'): for child in obj.children: set_parent_keep_world(child, obj.parent)
def poly2_area2(ps: Sequence[Vector])
-
Expand source code
def poly2_area2(ps: 'Sequence[Vector]'): # Площадь полигона, примерно, без учёта вогнутостей length = len(ps) if length < 3: return 0 elif length == 3: # Частый случай, оптимизация для треугольника return _area_tri(ps[0], ps[1], ps[2]) elif length == 4: # Частый случай, оптимизация для квада return _area_tri(ps[0], ps[1], ps[2]) + _area_tri(ps[0], ps[2], ps[3]) else: # Для пентагона и выше - Формула Гаусса s = ps[length - 1].x * ps[0].y - ps[0].x * ps[length - 1].y for i in range(length - 1): s += ps[i].x * ps[i + 1].y s -= ps[i + 1].x * ps[i].y return 0.5 * abs(s)
def remove_all_geometry(obj: Object)
-
Expand source code
def remove_all_geometry(obj: 'Object'): import bmesh # Очистка геометрии bm = bmesh.new() try: mesh = get_mesh_safe(obj) # Дегенеративные уебки, почему в Mesh нет API для удаления геометрии? bm.from_mesh(mesh) bm.clear() # TODO optimize? bm.to_mesh(mesh) finally: bm.free()
def remove_all_material_slots(obj: Object, slots=0)
-
Expand source code
def remove_all_material_slots(obj: 'Object', slots=0): while len(obj.material_slots) > slots: _C.view_layer.objects.active = obj ensure_op_finished(_bpy.ops.object.material_slot_remove(), name='bpy.ops.object.material_slot_remove')
def remove_all_vertex_colors(obj: Object)
-
Expand source code
def remove_all_vertex_colors(obj: 'Object'): mesh = get_mesh_safe(obj) while len(mesh.vertex_colors) > 0: mesh.vertex_colors.remove(mesh.vertex_colors[0])
def select_set_all(objects: Iterable[Object], state: bool)
-
Expand source code
def select_set_all(objects: 'Iterable[Object]', state: bool): for obj in objects: try: obj.hide_set(False) obj.select_set(state) except Exception as exc: _log.error(str(exc)) _log.error(repr(objects)) raise exc
def set_parent_keep_world(child: Object, parent: Object)
-
Expand source code
def set_parent_keep_world(child: 'Object', parent: 'Object'): m = child.matrix_world.copy() child.parent = parent child.parent_type = 'OBJECT' child.matrix_parent_inverse = _Matrix.Identity(4) child.matrix_world = m
Classes
class ConfigurationError (*args, **kwargs)
-
Unspecified run-time error.
Expand source code
class ConfigurationError(RuntimeError): # Ошибка конфигурации pass
Ancestors
- builtins.RuntimeError
- builtins.Exception
- builtins.BaseException
Subclasses
class KawaApplyParentInverseMatrices
-
Storage of an operator being executed, or registered after execution
Expand source code
class KawaApplyParentInverseMatrices(_bpy.types.Operator): bl_idname = "object.kawa_apply_parent_inverse_matrices" bl_label = "Apply Parent Inverse Transform Matricies" bl_options = {'REGISTER', 'UNDO'} @classmethod def poll(cls, context: 'Context'): if len(context.selected_objects) < 1: return False # Должны быть выбраны какие-то объекты if context.mode != 'OBJECT': return False # Требуется режим OBJECT return True def execute(self, context: 'Context'): applied = list(obj for obj in context.selected_objects if apply_parent_inverse_matrix(obj)) applied_strs = "".join("\n-\t{0}".format(repr(obj)) for obj in applied) self.report({'INFO'}, "Applied {0} parent inverse matrices:{1}".format(len(applied), applied_strs)) return {'FINISHED'} if len(applied) > 0 else {'CANCELLED'}
Ancestors
- bpy.types.Operator
- bpy.types.bpy_struct
Class variables
var bl_description : str
var bl_idname : str
var bl_label : str
var bl_options : Union[Set[str], Set[int]]
var bl_property : str
var bl_translation_context : str
var bl_undo_group : str
var has_reports : bool
var layout : bpy.types.UILayout
var macros : Union[Dict[str, bpy.types.Macro], List[bpy.types.Macro], bpy.types.bpy_prop_collection]
var name : str
var options : bpy.types.OperatorOptions
var properties : bpy.types.OperatorProperties
Static methods
def poll(context: Context)
-
Test if the operator can be called or not
:param context: :type context: 'Context'
Expand source code
@classmethod def poll(cls, context: 'Context'): if len(context.selected_objects) < 1: return False # Должны быть выбраны какие-то объекты if context.mode != 'OBJECT': return False # Требуется режим OBJECT return True
Methods
def execute(self, context: Context)
-
Execute the operator
:param context: :type context: 'Context' :rtype: typing.Union[typing.Set[str], typing.Set[int]] :return: result * RUNNING_MODAL Running Modal, Keep the operator running with blender. * CANCELLED Cancelled, The operator exited without doing anything, so no undo entry should be pushed. * FINISHED Finished, The operator exited after completing its action. * PASS_THROUGH Pass Through, Do nothing and pass the event on. * INTERFACE Interface, Handled but not executed (popup menus).
Expand source code
def execute(self, context: 'Context'): applied = list(obj for obj in context.selected_objects if apply_parent_inverse_matrix(obj)) applied_strs = "".join("\n-\t{0}".format(repr(obj)) for obj in applied) self.report({'INFO'}, "Applied {0} parent inverse matrices:{1}".format(len(applied), applied_strs)) return {'FINISHED'} if len(applied) > 0 else {'CANCELLED'}
class MaterialConfigurationError (mat, msg: str)
-
Unspecified run-time error.
Expand source code
class MaterialConfigurationError(ConfigurationError): def __init__(self, mat, msg: str): self.material = mat msg = 'Material={0}: {1}'.format(mat, msg) super().__init__(msg)
Ancestors
- ConfigurationError
- builtins.RuntimeError
- builtins.Exception
- builtins.BaseException
class SaveSelection (name=None)
-
A base class or mixin that enables context managers to work as decorators.
Expand source code
class SaveSelection(_contextlib.ContextDecorator): # TODO def __init__(self, name=None): self.last_active_object = None # type: Object self.shown = None # type: List[Object] self.selected = None # type: List[Object] def __enter__(self): self.last_active_object = _C.view_layer.objects.active self.selected = list(_C.view_layer.objects.selected) return self # def hide_set(self, obj: 'Object', state: 'bool'): # if self.hide_state is None: # self.hide_state = dict() # if obj not in self.hide_state.keys(): # self.hide_state[obj] = obj.hide_get() # obj.hide_set(state) # # def select_set(self, obj: 'Object', state: 'bool'): # if self.select_state is None: # self.select_state = dict() # if obj not in self.select_state.keys(): # self.select_state[obj] = obj.select_get() # obj.select_set(state) # # def activate_object(self, obj: 'Object'): # self.hide_set(obj, False) # self.select_set(obj, True) # _C.view_layer.objects.selected = obj # # def activate_objects(self, objs: 'Iterable[Object]'): # for obj in objs: # self.activate_object(obj) def __exit__(self, *exc): for obj in _C.view_layer.objects: obj.select_set(obj in self.selected) # if self.hide_state is not None: # for obj, state in self.hide_state.items(): # try: # obj.hide_set(state) # except ReferenceError: # pass # object invalid, this is fine # if self.select_state is not None: # for obj, state in self.select_state.items(): # try: # obj.select_set(state) # except ReferenceError: # pass # object invalid, this is fine try: _C.view_layer.objects.active = self.last_active_object except ReferenceError: pass # object invalid, this is fine
Ancestors
- contextlib.ContextDecorator