Search This Blog

Labels

Sunday, December 19, 2010

Blender/Python API Introduction

This API is a work-in-progress but is stable enough to start using with only minor changes to be made until the first stable release of Blender 2.5.

The Blender/Python API can do the following:

  • Edit any data the user interface can (Scenes, Meshes, Particles etc.)
  • Modify user preferences, keymaps and themes
  • Run tools with own settings
  • Create user interface elements such as menus, headers and panels
  • Create new tools
  • Create interactive tools
  • Create new rendering engines that integrate with Blender
  • Define new settings in existing Blender data
  • Draw in the 3D view using OpenGL commands from Python

The Blender/Python API can't (yet)...

  • Create new space types
  • Assign custom properties to every type
  • Define callbacks when Python-defined properties are adjusted

Before Starting

This document isn't intended to fully cover each topic. Rather, its purpose is to familiarize you with Blender 2.5's new Python API.

A quick list of helpful things to know before starting:

  • Blender uses Python 3.1; some 3rd party extensions are not available yet.
  • The interactive console in Blender 2.5 has been improved; testing one-liners in the console is a good way to learn.
  • For specific topics, see the Blender 2.55 Beta Python API Reference
  • Button tooltips show Python attributes and operator names.
  • Right clicking on buttons and menu items directly links to API documentation.
  • For more examples, the text menu has a templates section where some example operators can be found.
  • To examine further scripts distributed with Blender, see .blender/scripts/ui for the user interface and .blender/scripts/op for operators.

Key Concepts

Data Access
Accessing datablocks

Python accesses Blender's data in the same way as the animation system and user interface, which means any setting that is changed via a button can also be changed from Python.

Accessing data from the currently loaded blend file is done with the module bpy.data. This gives access to library data. For example:

>>> bpy.data.objects
<bpy_collection[2], Main.objects>  
>> bpy.data.scenes
<bpy_collection[1], Main.scenes>  
>>> bpy.data.materials
<bpy_collection[0], Main.materials>


About Collections

You'll notice that an index as well as a string can be used to access members of the collection.

Unlike Python's dictionaries, both methods are acceptable; however, the index of a member may change while running Blender.

>>> list(bpy.data.objects)
[bpy.data.objects["Cube"], bpy.data.objects["Plane"]]  
>>> bpy.data.objects['Cube']
bpy.data.objects["Cube"]  
>>> bpy.data.objects[0]
bpy.data.objects["Cube"]


Accessing attributes

Once you have a datablock such as a material, object, groups etc., its attributes can be accessed just like changing a setting in the interface; in fact, the button tooltip also displays the Python attribute which can help in finding what settings to change in a script.

>>> bpy.data.objects[0].name 
"Cube"  
>>> bpy.data.scenes["Scene"]
<bpy_struct Scene("Scene")>  
>>> bpy.data.materials.new("MyMaterial")
<bpy_struct Material("MyMaterial")>

For testing what data to access it's useful to use the "Console", which is its own space type in Blender 2.5. This supports auto-complete, giving you a fast way to dig into different data in your file.

Example: data path that can be quickly found via the console:

# scene.render_data - > scene.render (since rev 27104)
#>>> bpy.data.scenes[0].render_data.resolution_percentage  
>>> bpy.data.scenes[0].render.resolution_percentage
100
>>> bpy.data.scenes[0].objects["Torus"].data.vertices[0].co.x
1.0


Custom Properties

Python can access properties on any datablock that has an ID (data that can be linked in and accessed from bpy.data.*).

When assigning a property, you can make up your own names, these will be created when needed or overwritten if they exist.

This data is saved with the blend file and copied with objects.

Example:

bpy.context.object["MyOwnProperty"] = 42   if "SomeProp" in bpy.context.object:
print("Property found")  # Use the get function like a Python dictionary which can have a fallback value.
value = bpy.data.scenes["Scene"].get("test_prop", "fallback value")  
# dictionaries can be assigned as long as they only use basic types.
group = bpy.data.groups.new("MyTestGroup")
group["GameSettings"] = {"foo": 10, "bar": "spam", "baz": {}}  
del group["GameSettings"]

Note that these properties can only be assigned basic Python types.


  • int, float, string
  • array of ints/floats
  • dictionary (only string keys types on this list)

These properties are valid outside of Python. They can be animated by curves or used in driver paths.


Context

While it's useful to be able to access data directly by name or as a list, it's more common to operate on the user's selection. The context is always available from bpy.context and can be used to get the active object, scene, tool settings along with many other attributes.

Common-use cases...

 >>> bpy.context.object
>>> bpy.context.selected_objects
>>> bpy.context.visible_bones

Note that the context is read-only. These values cannot be modified directly, though they may be changed by running API functions or by using the data API. So bpy.context.object = obj will raise an error. Butbpy.context.scene.objects.active = obj will work as expected.

The context attributes change depending on where it is accessed. The 3D view has different context members to the Console, so take care when accessing context attributes that the user state is known.


Operators (Tools)

Operators are tools generally accessed by the user from buttons, menu items or key shortcuts. From the user perspective they are a tool but Python can run these with its own settings through the bpy.opsmodule.

Examples...

 >>> bpy.ops.mesh.flip_normals()
{'FINISHED'}
>>> bpy.ops.mesh.hide(unselected=False)
{'FINISHED'}
>>> bpy.ops.object.scale_apply()
{'FINISHED'}

Operator Cheat Sheet

"Help -> Operator Cheat Sheet" gives a list of all operators and their default values in Python syntax, along with the generated docs. This is a good way to get an overview of Blender's operators.


Operator Poll()

Some operators have "poll" function which may check that the mouse is a valid area or that the object is in the correct mode (Edit Mode, Weight Paint etc).

When an operators poll function fails with python an exception is raised.

For example, calling bpy.ops.view3d.render_border() from the console gives an error:

SystemError: _bpy.ops.call: operator bpy.ops.view3d.render_border.poll() function failed, context is incorrect.

In this case the context must be the 3d view with an active camera.


Integration


Python scripts can integrate with Blender in the following ways:


  • By defining a rendering engine
  • By defining operators
  • By defining menus, headers and panels
  • By inserting new buttons into existing menus, headers and panels

In Python, this is done by defining a class, which is a subclass of an existing type.


Example Operator
import bpy
class HelloWorld(bpy.types.Operator):
bl_idname = "screen.hello_world"
bl_label = "Hello World"   def execute(self, context):
self.report({'WARNING'}, "Hello World")
return {'FINISHED'}   #latest version not needed: bpy.types.register(HelloWorld)
if __name__ == "__main__":
bpy.ops.screen.hello_world()

Once this script runs, HelloWorld is registered with Blender and can be called from the operator search popup or added to the toolbar.

Notice the class members with the bl_ prefix, these are documented in the Python API.http://www.blender.org/documentation/250PythonDoc/bpy.types.Operator.html#bpy.types.Operator.bl_idname


Example Panel

Panels register themselves as a class, like an operator. Notice the extra variables used to set the context they display in.

import bpy   class OBJECT_PT_hello(bpy.types.Panel):
bl_label = "Hello World Panel"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "object"   def draw(self, context):
layout = self.layout   obj = context.object   row = layout.row()
row.label(text="Hello world!", icon='WORLD_DATA')   row = layout.row()
row.label(text="Active object is: " + obj.name)
row = layout.row()
row.prop(obj, "name")   #latest version not needed: bpy.types.register(OBJECT_PT_hello)

To run the script:


  1. Highlight the above code then press CtrlC to copy it
  2. Start Blender
  3. Press Ctrl→ twice to change to the Scripting layout
  4. Press CtrlV to paste the code into the text panel (the upper left frame)
  5. Click Run Script or press AltP to run the script

To view the results:


  1. RMB File:Template-RMB.png-click on the default cube to select it
  2. LMB File:Template-LMB.png-click the Object properties icon in the buttons panel (far right; appears as a tiny cube)
  3. Scroll down to see a panel named Hello World Panel
  4. Changing the object name also updates Hello World Panel's Name: field

Note the row distribution and the label and properties that are available through the code.

See also the Panel class documentation


Types


Blender defines a number of Python types but also uses Python native types.

Blender's Python API can be split up into 3 categories.


Native Types

In simple cases returning a number or a string as a custom type would be cumbersome, so these are accessed as normal python types.


  • blender float/int/boolean -> float/int/boolean
  • blender enumerator -> string
 >>> C.object.rotation_mode = 'AXIS_ANGLE'


  • blender enumerator (multiple) -> set of strings
 # within an operator
self.report({'WARNING', 'INFO'}, "SomeMessage")


Internal Types

used for Blender datablocks and collections.
Reference: http://www.blender.org/documentation/250PythonDoc/bpy.types.bpy_struct.html

For data that contains its own attributes groups/meshes/bones.

There are 2 main types that wrap Blenders data, one for datablocks (known internally as bpy_struct), another for properties.

 >>> C.object
<bpy_struct, Object("Cube")>   >>> C.scene.objects
<bpy_collection[3], Scene.objects>

Note that these types keep a reference to Blender's data so modifying them should be immediately visible.


Mathutils Types

used for vectors, quaternion, eulers, matrix and color types.
Reference: http://www.blender.org/documentation/250PythonDoc/mathutils.html

Some attributes such as object.location, bone.rotation_euler and scene.cursor_location can be accessed as special math types which can be added together and manipulated in useful ways.

Example of a matrix, vector multiplication:

bpy.context.object.data.verts[0].co * bpy.context.object.matrix_world

Note: Mathutils types keep a reference to Blender's internal data so changes can be applied back.

Note: Since Blender 2.4x, Mathutils types now use all radians rather than degrees.

Example

bpy.context.object.location.z += 2.0


Script Initialization


All scripts in known directories scripts/ui, scripts/io, scripts/op are imported (not run) on startup. Use F8 to reload these scripts.

1 comment:

  1. I found this post while looking for a solution to an issue I'm having with the BGE and contexts. I posted at Blender Artists and haven't gotten a response. I'm hoping you might know the fix. You can view the problem description here: http://blenderartists.org/forum/showthread.php?226088-Is-it-possible-to-run-Blender-Game-Engine-in-background-mode

    Thanks so much for any help!

    ReplyDelete