from typing import Any, List, Tuple
import numpy as np
import bpy
from blenderproc.python.utility.Utility import Utility, KeyFrame
from mathutils import Vector, Euler, Color, Matrix, Quaternion
import weakref
[docs]class Struct:
# Contains weak refs to all struct instances
# As it only uses weak references, instances can still be removed by GC when all other references are gone.
# If that happens, the instances' weak ref is also automatically removed from the set
__refs__: weakref.WeakSet = weakref.WeakSet()
def __init__(self, bpy_object: bpy.types.Object):
self.blender_obj = bpy_object
# Remember that this instance exists
Struct.__refs__.add(self)
[docs] def is_valid(self):
""" Check whether the contained blender reference is valid.
The reference might become invalid after an undo operation or when the referenced struct is deleted.
:return: True, if it is valid.
"""
return str(self.blender_obj) != "<bpy_struct, Object invalid>"
[docs] def set_name(self, name: str):
""" Sets the name of the struct.
:param name: The new name.
"""
self.blender_obj.name = name
[docs] def get_name(self) -> str:
""" Returns the name of the struct.
:return: The name.
"""
return self.blender_obj.name
[docs] def get_cp(self, key: str, frame: int = None) -> Any:
""" Returns the custom property with the given key.
:param key: The key of the custom property.
:param frame: The frame number at which the value should be returned. If None is given, the current frame number is used.
:return: The value of the custom property.
"""
with KeyFrame(frame):
value = self.blender_obj[key]
if isinstance(value, (Vector, Euler, Color, Matrix, Quaternion)):
value = np.array(value)
return value
[docs] def set_cp(self, key: str, value: Any, frame: int = None):
""" Sets the custom property with the given key.
Keyframes can be only set for custom properties for the types int, float or bool.
:param key: The key of the custom property.
:param value: The value to set.
:param frame: The frame number which the value should be set to. If None is given, the current frame number is used.
"""
self.blender_obj[key] = value
if isinstance(self.blender_obj[key], float) or isinstance(self.blender_obj[key], int):
Utility.insert_keyframe(self.blender_obj, "[\"" + key + "\"]", frame)
[docs] def del_cp(self, key: str):
""" Removes the custom property with the given key.
:param key: The key of the custom property to remove.
"""
del self.blender_obj[key]
[docs] def has_cp(self, key: str) -> bool:
""" Return whether a custom property with the given key exists.
:param key: The key of the custom property to check.
:return: True, if the custom property exists.
"""
return key in self.blender_obj
[docs] def get_all_cps(self) -> list:
""" Returns all custom properties as key, value pairs.
:return: A list of key value pairs
"""
return self.blender_obj.items()
[docs] def clear_all_cps(self):
""" Removes all existing custom properties the struct has. """
# iterating over the keys is not possible as deleting them changes the structure of the
# underlying blender object -> to solve this we always remove only the first element until no element is left
while len(self.blender_obj.keys()) > 0:
# extract first element of the keys
key = list(self.blender_obj.keys())[0]
# delete this first element
del self.blender_obj[key]
[docs] def get_attr(self, attr_name: str) -> Any:
""" Returns the value of the attribute with the given name.
:param attr_name: The name of the attribute.
:return: The value of the attribute
"""
if hasattr(self.blender_obj, attr_name):
value = getattr(self.blender_obj, attr_name)
if isinstance(value, (Vector, Euler, Color, Matrix, Quaternion)):
value = np.array(value)
return value
else:
raise Exception("This element does not have an attribute " + str(attr_name))
def __setattr__(self, key: str, value: Any):
if key != "blender_obj":
raise Exception("The API class does not allow setting any attribute. Use the corresponding method or directly access the blender attribute via entity.blender_obj.attribute_name")
else:
object.__setattr__(self, key, value)