.. _blender_mesh: Context Managers ================ If possible, `one should avoid relying on Blender operators `__, but sometimes they are just too useful to pass over. In those cases, it is necessary to manage the context and modes explicitly. Blender provides `bpy.types.Context.temp_override `__ to manage the context. As for modes, Blender provides `bpy.ops.object.mode_set `__ and `bpy.ops.object.mode_set_with_submode `__. These operators are wrapped by the :py:func:`~ddg.visualization.blender.context.mode` context manager. .. doctest:: >>> import bpy >>> from ddg.visualization.blender.context import mode >>> >>> bpy.ops.mesh.primitive_cube_add() {'FINISHED'} >>> cube = bpy.context.object >>> with mode(cube, mode="EDIT", exit_mode="OBJECT"): ... print(f"Mode within the context: {cube.mode}") ... Mode within the context: EDIT >>> print(f"Mode after exiting the context: {cube.mode}") Mode after exiting the context: OBJECT Note that Blender doesn't write changes to edit meshes to the mesh until the object exits edit mode. .. doctest:: >>> import bmesh >>> import numpy as np >>> >>> co_original = np.vstack([v.co for v in cube.data.vertices]) >>> with mode(cube, mode="EDIT", exit_mode="OBJECT"): ... bm = bmesh.from_edit_mesh(cube.data) ... for v in bm.verts: ... v.co *= 3 ... bmesh.update_edit_mesh(cube.data) ... co_in_context = np.vstack([v.co for v in cube.data.vertices]) ... # Note that even after calling bmesh.update_edit_mesh, ... # cube.data.vertices coordinates haven't changed. ... # The changes are visible in the UI however! ... assert np.allclose(co_original, co_in_context) ... Upon exiting the context, the mode is set to OBJECT mode. Since this is a different mode from EDIT mode, the changes made in EDIT mode are written to `cube.data`. .. doctest:: >>> co_after_exit = np.vstack([v.co for v in cube.data.vertices]) >>> assert np.allclose(3 * co_original, co_after_exit) The above example motivates :py:func:`~ddg.visualization.blender.bmesh.bmesh_from_mesh`, yet another context manager. It creates a BMesh from a mesh and automatically calls ``bmesh.update_edit_mesh`` or ``bmesh.to_mesh`` depending on whether the mesh is in edit mode or not. In the latter case, it also ensures that the BMesh is freed. The above example (without the assertions) can be refactored as such : .. doctest:: >>> from ddg.visualization.blender.bmesh import bmesh_from_mesh >>> with mode(cube, mode="EDIT", exit_mode="OBJECT"): ... with bmesh_from_mesh(cube.data) as bm: ... for v in bm.verts: ... v.co *= 3 ...