Dev:PT/2.5/Py/API/Intro

提供: wiki
移動先: 案内検索

Introdução à Blender/Python API

Esta API está em desenvolvimento mas está estável o suficiente para começar a ser usada com apenas pequenas mudanças a serem feitas até à primeira versão estável do Blender 2.5.

A Blender/Python API pode fazer o seguinte:

  • Editar qualquer data de User Interface(Scenes, Meshes, Particles etc.)
  • Modificar as preferências do utilizador, atalhos do teclado e temas
  • Correr ferramentas com as próprias definições
  • Criar elementos do User Interface como menus, cabeçalhos e painéis
  • Criar novas ferramentas
  • Criar ferramentas interactivas
  • Criar novas Render Engines que se integram com o Blender
  • Definir novas definições na Blender data existente
  • Desenhar na 3D view usando comandos OpenGL do Python

A Blender/Python API não consegue (por enquanto)...

  • Criar novos space types
  • Atribuir Custom Properties a todos os tipos
  • Definir Callbacks quando as Python-defined properties estão ajustadas

Antes de começar

Este documento não pretende cobrir totalmente cada tópico. O seu propósito é dar-lhe a conhecer a Blender 2.5 Python API.

Uma rápida lista the coisas a saber antes de começar:

  • O Blender usa o Python 3.1; algumas 3rd party extensions não estão disponíveis por enquanto.
  • A consola do Blender 2.5 foi melhorada; testar one-liners na consola é uma boa maneira de aprender.
  • Para tópicos específicos, veja Blender 2.72 Python API Reference
  • Button tooltips mostram atributos Python e nomes de operadores.
  • Right clicking em butões e em menu items ligam directamente à documentação da API.
  • Para mais exemplos, o text menu tem uma templates section onde alguns exemplos de operadores podem ser encontrados.
  • Para examinar outros scripts distribuídos com o Blender, veja .blender/scripts/ui para o User Interface e .blender/scripts/op para operadores.

Conceptos chave

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[6], BlendDataObjects>

>> bpy.data.scenes
<bpy_collection[1], BlendDataScenes>

>>> bpy.data.materials
<bpy_collection[1], BlendDataMaterials>

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. But bpy.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.

See bpy.context API reference

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.ops module.

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 a "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 operator's poll function fails within python, an exception is raised.

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

RuntimeError: Operator bpy.ops.view3d.render_border.poll() 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 OpHelloWorld(bpy.types.Operator):
    bl_idname = "screen.hello_world"
    bl_label = "Hello World"
     
    def execute(self, context):
        self.report({'WARNING'}, "Hello World")
        return {'FINISHED'}

# registering and menu integration
def register():
    bpy.utils.register_class(OpHelloWorld)

# unregistering and removing menus
def unregister():
    bpy.utils.unregister_class(OpHelloWorld)

if __name__ == "__main__":
    register()

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

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
  6. Click in the 3dview, press spacebar for the search menu, and type "hello"
  7. click on the "Hello World" item found in search


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

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")
 
# registering and menu integration
def register():
    bpy.utils.register_class(OBJECT_PT_hello)
 
# unregistering and removing menus
def unregister():
    bpy.utils.unregister_class(OBJECT_PT_hello)
 
if __name__ == "__main__":
    register()

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 Template-RMB.png-click on the default cube to select it
  2. LMB 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.data.objects["Cube"]

 >>> C.scene.objects
bpy.data.scenes["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.

Examples

# modifies Z in place.
bpy.context.object.location.z += 2.0

# location variable holds a reference to the object too.
location = bpy.context.object.location
location *= 2.0

# Copying the value drops the reference.
location = bpy.context.object.location.copy()

Animation

There are 2 ways to add keyframes through Python. The first is through key properties directly, which is similar to inserting a keyframe as a user. You can also manually create the curves and keyframe data, then set the path to the property. Here are examples of both methods.

These 2 examples insert a keyframe on the active object's Z axis.

Simple example:

obj = bpy.context.object
obj.location[2] = 0.0
obj.keyframe_insert(data_path="location", frame=10.0, index=2)
obj.location[2] = 1.0
obj.keyframe_insert(data_path="location", frame=20.0, index=2)

Using Low-Level Functions:

obj = bpy.context.object
obj.animation_data_create()
obj.animation_data.action = bpy.data.actions.new(name="MyAction")
fcu_z = obj.animation_data.action.fcurves.new(data_path="location", index=2)
fcu_z.keyframe_points.add(2)
fcu_z.keyframe_points[0].co = 10.0, 0.0
fcu_z.keyframe_points[1].co = 20.0, 1.0

Style Conventions

For Blender 2.5 we have chosen to follow python suggested style guide to avoid mixing styles amongst our own scripts and make it easier to use python from other projects.

Using our style guide for your own scripts makes it easier if you eventually want to contribute them to blender.

This style guide is known as pep8 and can be found here.

A brief listing of pep8 criteria.

  • camel caps for class names: MyClass
  • all lower case underscore separated module names: my_module
  • indentation of 4 spaces (no tabs)
  • spaces around operators. 1 + 1, not 1+1
  • only use explicit imports, (no importing '*')
  • don't use single line: if val: body, separate onto 2 lines instead.

As well as pep8 we have other conventions used for blender python scripts.

  • Use single quotes for enums, and double quotes for strings.
    Both are of course strings but in our internal API enums are unique items from a limited set. eg.
    bpy.context.scene.render.file_format = 'PNG'
    bpy.context.scene.render.filepath = "//render_out"
  • pep8 also defines that lines should not exceed 79 characters, we felt this is too restrictive so this is optional per script.

Periodically we run checks for pep8 compliance on blender scripts, for scripts to be included in this check add this line as a comment at the top of the script.

# <pep8 compliant>

To enable line length checks use this instead.

# <pep8-80 compliant>