| name | blender-python-scripting |
| description | Blender 5.x Python scripting (bpy) — custom operators, UI panels, add-on development, context management, handlers, timers, property system, batch processing, and data model access. Now targets Python 3.13 (Blender 5.1). |
Blender Python Scripting Expert
Overview
This skill provides expert guidance for Blender 5.x Python scripting: writing custom operators, building UI panels, developing add-ons, managing context, using handlers and timers, working with the property system, batch processing, and accessing Blender's data model. The reference files contain complete templates and the data model hierarchy.
MCP-First Approach
Prefer the official Blender MCP Server (Blender Lab, Blender 5.1+) for executing scripts, inspecting datablocks, mutating scene state directly in a running Blender session. Fall back to emitting Python scripts only when the MCP server is not connected.
Detection: at session start, look for tools whose names match execute_blender_code, get_objects_summary, or search_api_docs — these belong to the official blender-mcp server. If any are present, MCP is available.
Tools used by this skill:
execute_blender_code — run bpy Python in the live session (primary action)
get_objects_summary, get_object_detail_summary — inspect scene state before mutating
get_blendfile_summary_datablocks / _missing_files / _of_linked_libraries / _path_info / _usage_guess — inventory the file
search_api_docs, get_python_api_docs, search_manual_docs — confirm correct API/operator names before calling them
get_screenshot_of_window_as_image / _as_json, get_screenshot_of_area_as_image — verify visual results
jump_to_tab_by_name, jump_to_tab_by_space_type, jump_to_view3d_object_by_name, jump_to_view3d_object_data_by_name — drive the UI to make changes visible
render_thumbnail_to_path, render_viewport_to_path — capture quick visual feedback
Workflow: inspect with a get_* / search_* tool → mutate via execute_blender_code → verify with a screenshot or summary tool. Keep code blocks small and idempotent so failures are easy to localize.
Setup: see docs/blender-mcp-setup.md.
Task Decision Tree
- "Create a custom operator" -> Consult
references/python_api.md for operator templates
- "Build a UI panel" -> See Panel templates in
references/python_api.md
- "Develop a Blender add-on" -> Follow the Add-on Structure section below
- "Batch process files / renders" -> See Batch Processing patterns
- "Access scene/object data" -> Consult
references/data_model_reference.md
- "Register a property" -> See Property System in
references/python_api.md
- "Run code on frame change / file load" -> See Handler patterns in
references/python_api.md
- "Context errors / poll failures" -> See Context Management below
Blender 5.1 Python Changes
Python 3.13 Upgrade
- Blender 5.1 bundles Python 3.13 (up from 3.12 in 5.0)
- Update
bl_info["blender"] to (5, 1, 0) for version gating in add-ons
- Use type hint syntax (
int | None instead of Optional[int]) — now fully supported
- Performance improvements in CPython 3.13: faster function calls, better GC
Removed Operators
bpy.ops.anim.convert_legacy_action — removed; layered actions handle this automatically
API Additions
bpy.ops.pose.apply_to_basis() — bake current pose as rest pose
- Node socket types:
NodeSocketFont for font data
- Volume grid node types for geometry nodes (see blender-geometry-nodes skill)
Add-on Structure
A proper Blender add-on follows this layout:
my_addon/
├── __init__.py # bl_info, register(), unregister()
├── operators.py # Custom operators
├── panels.py # UI panels
├── properties.py # PropertyGroups
├── preferences.py # AddonPreferences (optional)
└── utils.py # Shared utilities
Minimal Single-File Add-on
bl_info = {
"name": "My Add-on",
"author": "Author",
"version": (1, 0, 0),
"blender": (5, 0, 0),
"location": "View3D > Sidebar > My Tab",
"description": "Short description",
"category": "Object",
}
import bpy
class MY_OT_my_operator(bpy.types.Operator):
bl_idname = "my.my_operator"
bl_label = "My Operator"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
return context.active_object is not None
def execute(self, context):
return {'FINISHED'}
class MY_PT_my_panel(bpy.types.Panel):
bl_label = "My Panel"
bl_idname = "MY_PT_my_panel"
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = "My Tab"
def draw(self, context):
layout = self.layout
layout.operator("my.my_operator")
classes = (MY_OT_my_operator, MY_PT_my_panel)
def register():
for cls in classes:
bpy.utils.register_class(cls)
def unregister():
for cls in classes:
bpy.utils.unregister_class(cls)
if __name__ == "__main__":
register()
Naming Conventions
- Operators:
ADDON_OT_name → bl_idname = "addon.name"
- Panels:
ADDON_PT_name
- Menus:
ADDON_MT_name
- UILists:
ADDON_UL_name
- PropertyGroups:
ADDON_PG_name (convention, not enforced)
- Headers:
ADDON_HT_name
Context Management
Common Context Issues
Blender operations depend on the current context (active object, mode, area type). Many bpy.ops.* calls will fail if context is wrong.
Rule: Always check context before calling operators. Use poll() methods to guard operators.
Context Override (Blender 4.x+ / 5.x)
with bpy.context.temp_override(active_object=obj, selected_objects=[obj]):
bpy.ops.object.delete()
area = next(a for a in bpy.context.screen.areas if a.type == 'VIEW_3D')
region = next(r for r in area.regions if r.type == 'WINDOW')
with bpy.context.temp_override(area=area, region=region):
bpy.ops.view3d.camera_to_view()
Mode Switching
if bpy.context.object and bpy.context.object.mode != 'EDIT':
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.object.mode_set(mode='OBJECT')
Common Context Gotchas
bpy.ops in background mode: Many viewport operators fail in --background mode. Use direct data API (bpy.data.*) instead.
- Operator context in handlers: Handlers run outside normal context. Use
bpy.context.temp_override() or avoid bpy.ops in handlers.
- Active object is None: Always check
context.active_object is not None before operations.
- Wrong mode: Object-mode operators fail in edit mode and vice versa. Check
context.object.mode.
- Depsgraph not updated: After modifying data, call
bpy.context.view_layer.update() or depsgraph.update() if needed.
display_type values depend on object type: 'PLAIN_AXES' is only valid for Empties. Mesh objects use 'BOUNDS', 'WIRE', 'SOLID', or 'TEXTURED'.
Imported asset probes
When importing external FBX/DAE assets into an existing procedural scene, snapshot objects before import and transform only the newly imported objects. Relink stale texture references (especially missing .fbm folders from FBX exports) to local PNGs, compute bounds from matrix_world @ bound_box, and verify every transform with an actual still render. See references/imported-game-assets.md for the proven probe/relink/orientation workflow.
Batch Processing
Batch Render
import bpy, os
scenes_dir = "/path/to/blend/files"
output_dir = "/path/to/output"
for filename in os.listdir(scenes_dir):
if filename.endswith(".blend"):
filepath = os.path.join(scenes_dir, filename)
bpy.ops.wm.open_mainfile(filepath=filepath)
bpy.context.scene.render.filepath = os.path.join(
output_dir, filename.replace(".blend", ".png")
)
bpy.ops.render.render(write_still=True)
Batch Object Processing
import bpy
for obj in bpy.data.objects:
if obj.type == 'MESH':
bpy.ops.object.select_all(action='DESELECT')
obj.select_set(True)
bpy.context.view_layer.objects.active = obj
for mod in obj.modifiers:
bpy.ops.object.modifier_apply(modifier=mod.name)
Command-Line Batch
blender --background scene.blend --python my_script.py
blender --background --python batch_render.py -- --input /files --output /renders
Access custom args after --:
import sys
argv = sys.argv[sys.argv.index("--") + 1:]
Debugging Tips
- System Console: Window > Toggle System Console (Windows) or run Blender from terminal
- Python Console: Open the Python Console editor area for interactive testing
- Info Editor: Shows Python equivalents of GUI actions — useful for discovering API calls
dir() and help(): Use in Python Console to explore API objects
bpy.context.copy(): Returns a dict of the current context for inspection
- Error in modal operator: Add
print() statements or use traceback.print_exc() in exception handlers
Property System Quick Reference
| Type | Registration | Values |
|---|
BoolProperty | bpy.props.BoolProperty() | True/False |
IntProperty | bpy.props.IntProperty() | min, max, default, step |
FloatProperty | bpy.props.FloatProperty() | min, max, default, precision |
StringProperty | bpy.props.StringProperty() | default, maxlen, subtype |
EnumProperty | bpy.props.EnumProperty() | items=[(id, name, desc)] |
FloatVectorProperty | bpy.props.FloatVectorProperty() | size, default, subtype |
IntVectorProperty | bpy.props.IntVectorProperty() | size, default |
BoolVectorProperty | bpy.props.BoolVectorProperty() | size, default |
PointerProperty | bpy.props.PointerProperty() | type=PropertyGroup |
CollectionProperty | bpy.props.CollectionProperty() | type=PropertyGroup |
Property Subtypes
- Float/Int:
'PIXEL', 'PERCENTAGE', 'FACTOR', 'ANGLE', 'TIME', 'DISTANCE'
- FloatVector:
'COLOR', 'TRANSLATION', 'DIRECTION', 'VELOCITY', 'ACCELERATION', 'EULER', 'QUATERNION', 'XYZ'
- String:
'FILE_PATH', 'DIR_PATH', 'FILE_NAME', 'BYTE_STRING', 'PASSWORD'
Python API Reference
For complete templates including operators (basic, modal, file dialog, invoke with props), panels (conditional, sub-panels, collapsible), PropertyGroups, handlers, timers, keymaps, and context overrides, consult references/python_api.md.
Data Model Reference
For the complete Blender data hierarchy (bpy.data.*), ID types, object types, mesh/curve/armature data access patterns, and depsgraph usage, consult references/data_model_reference.md.