forked from bartvdbraak/blender
API docs: intro overhaul
- Update terminology, spelling, formatting. - Rename screen to workspace. - Update for 2.8 UI changes.
This commit is contained in:
parent
0aa7c11788
commit
23f8ab4250
@ -427,7 +427,7 @@ offers a set of extensive examples, including advanced features.
|
||||
|
||||
Return evaluator parameters
|
||||
|
||||
.. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glGetMap.xml>`_
|
||||
.. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glGetMap.xml>`__
|
||||
|
||||
:type target: Enumerated constant
|
||||
:arg target: Specifies the symbolic name of a map.
|
||||
|
@ -1,5 +1,5 @@
|
||||
..
|
||||
This document is appended to the auto generated bmesh api doc to avoid clogging up the C files with details.
|
||||
This document is appended to the auto generated BMesh API doc to avoid clogging up the C files with details.
|
||||
to test this run:
|
||||
./blender.bin -b -noaudio -P doc/python_api/sphinx_doc_gen.py -- \
|
||||
--partial bmesh* ; cd doc/python_api ; sphinx-build sphinx-in sphinx-out ; cd ../../
|
||||
@ -19,25 +19,24 @@ Submodules:
|
||||
Introduction
|
||||
------------
|
||||
|
||||
This API gives access the blenders internal mesh editing api, featuring geometry connectivity data and
|
||||
This API gives access the Blender's internal mesh editing API, featuring geometry connectivity data and
|
||||
access to editing operations such as split, separate, collapse and dissolve.
|
||||
|
||||
The features exposed closely follow the C API,
|
||||
giving python access to the functions used by blenders own mesh editing tools.
|
||||
giving Python access to the functions used by Blender's own mesh editing tools.
|
||||
|
||||
For an overview of BMesh data types and how they reference each other see:
|
||||
`BMesh Design Document <https://wiki.blender.org/index.php/Dev:Source/Modeling/BMesh/Design>`_ .
|
||||
`BMesh Design Document <https://wiki.blender.org/index.php/Dev:Source/Modeling/BMesh/Design>`__.
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
**Disk** and **Radial** data is not exposed by the python api since this is for internal use only.
|
||||
**Disk** and **Radial** data is not exposed by the Python API since this is for internal use only.
|
||||
|
||||
|
||||
.. warning:: TODO items are...
|
||||
|
||||
* add access to BMesh **walkers**
|
||||
* add custom-data manipulation functions add/remove/rename.
|
||||
- add access to BMesh **walkers**.
|
||||
- add custom-data manipulation functions add, remove or rename.
|
||||
|
||||
|
||||
Example Script
|
||||
@ -46,55 +45,52 @@ Example Script
|
||||
.. literalinclude:: __/__/__/release/scripts/templates_py/bmesh_simple.py
|
||||
|
||||
|
||||
Stand-Alone Module
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
Standalone Module
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
The bmesh module is written to be standalone except for :mod:`mathutils`
|
||||
The BMesh module is written to be standalone except for :mod:`mathutils`
|
||||
which is used for vertex locations and normals.
|
||||
|
||||
The only other exception to this are when converting mesh data to and from :class:`bpy.types.Mesh`.
|
||||
|
||||
|
||||
Mesh Access
|
||||
-----------
|
||||
|
||||
There are 2 ways to access BMesh data, you can create a new BMesh by converting a mesh from
|
||||
:class:`bpy.types.BlendData.meshes` or by accessing the current edit mode mesh.
|
||||
see: :class:`bmesh.types.BMesh.from_mesh` and :mod:`bmesh.from_edit_mesh` respectively.
|
||||
There are two ways to access BMesh data, you can create a new BMesh by converting a mesh from
|
||||
:class:`bpy.types.BlendData.meshes` or by accessing the current Edit-Mode mesh.
|
||||
See: :class:`bmesh.types.BMesh.from_mesh` and :mod:`bmesh.from_edit_mesh` respectively.
|
||||
|
||||
When explicitly converting from mesh data python **owns** the data, that is to say -
|
||||
that the mesh only exists while python holds a reference to it,
|
||||
and the script is responsible for putting it back into a mesh data-block when the edits are done.
|
||||
When explicitly converting from mesh data Python **owns** the data, that means that
|
||||
the mesh only exists while Python holds a reference to it.
|
||||
The script is responsible for putting it back into a mesh data-block when the edits are done.
|
||||
|
||||
Note that unlike :mod:`bpy`, a BMesh does not necessarily correspond to data in the currently open blend file,
|
||||
Note that unlike :mod:`bpy`, a BMesh does not necessarily correspond to data in the currently open blend-file,
|
||||
a BMesh can be created, edited and freed without the user ever seeing or having access to it.
|
||||
Unlike edit mode, the bmesh module can use multiple BMesh instances at once.
|
||||
Unlike Edit-Mode, the BMesh module can use multiple BMesh instances at once.
|
||||
|
||||
Take care when dealing with multiple BMesh instances since the mesh data can use a lot of memory, while a mesh that
|
||||
python owns will be freed in when the script holds no references to it,
|
||||
its good practice to call :class:`bmesh.types.BMesh.free` which will remove all the mesh data immediately and disable
|
||||
further access.
|
||||
Take care when dealing with multiple BMesh instances since the mesh data can use a lot of memory.
|
||||
While a mesh that the Python script owns will be freed when the script holds no references to it,
|
||||
it's good practice to call :class:`bmesh.types.BMesh.free` which will remove all the mesh data immediately
|
||||
and disable further access.
|
||||
|
||||
|
||||
EditMode Tessellation
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
Edit-Mode Tessellation
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
When writing scripts that operate on editmode data you will normally want to re-calculate the tessellation after
|
||||
running the script, this needs to be called explicitly.
|
||||
|
||||
The BMesh its self does not store the triangulated faces, they are stored in the :class:`bpy.types.Mesh`,
|
||||
When writing scripts that operate on Edit-Mode data you will normally want to re-calculate the tessellation after
|
||||
running the script, this needs to be called explicitly.
|
||||
The BMesh itself does not store the triangulated faces, instead they are stored in the :class:`bpy.types.Mesh`,
|
||||
to refresh tessellation triangles call :class:`bpy.types.Mesh.calc_loop_triangles`.
|
||||
|
||||
|
||||
CustomData Access
|
||||
-----------------
|
||||
|
||||
BMesh has a unified way to access mesh attributes such as UV's vertex colors, shape keys, edge crease etc.
|
||||
BMesh has a unified way to access mesh attributes such as UVs, vertex colors, shape keys, edge crease, etc.
|
||||
This works by having a **layers** property on BMesh data sequences to access the custom data layers
|
||||
which can then be used to access the actual data on each vert, edge, face or loop.
|
||||
|
||||
This works by having a **layers** property on bmesh data sequences to access the custom data layers which can then be
|
||||
used to access the actual data on each vert/edge/face/loop.
|
||||
|
||||
Here are some examples ...
|
||||
Here are some examples:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -139,27 +135,27 @@ Here are some examples ...
|
||||
Keeping a Correct State
|
||||
-----------------------
|
||||
|
||||
When modeling in blender there are certain assumptions made about the state of the mesh.
|
||||
When modeling in Blender there are certain assumptions made about the state of the mesh:
|
||||
|
||||
* hidden geometry isn't selected.
|
||||
* when an edge is selected, its vertices are selected too.
|
||||
* when a face is selected, its edges and vertices are selected.
|
||||
* duplicate edges / faces don't exist.
|
||||
* faces have at least 3 vertices.
|
||||
- Hidden geometry isn't selected.
|
||||
- When an edge is selected, its vertices are selected too.
|
||||
- When a face is selected, its edges and vertices are selected.
|
||||
- Duplicate edges / faces don't exist.
|
||||
- Faces have at least three vertices.
|
||||
|
||||
To give developers flexibility these conventions are not enforced,
|
||||
however tools must leave the mesh in a valid state else other tools may behave incorrectly.
|
||||
|
||||
yet tools must leave the mesh in a valid state or else other tools may behave incorrectly.
|
||||
Any errors that arise from not following these conventions is considered a bug in the script,
|
||||
not a bug in blender.
|
||||
not a bug in Blender.
|
||||
|
||||
|
||||
Selection / Flushing
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
As mentioned above, it is possible to create an invalid selection state
|
||||
(by selecting a state and then de-selecting one of its vertices's for example), mostly the best way to solve this is to
|
||||
flush the selection after performing a series of edits. this validates the selection state.
|
||||
(by selecting a state and then deselecting one of its vertices for example),
|
||||
mostly the best way to solve this is to flush the selection
|
||||
after performing a series of edits. This validates the selection state.
|
||||
|
||||
|
||||
Module Functions
|
||||
|
@ -3,94 +3,84 @@
|
||||
Reference API Usage
|
||||
*******************
|
||||
|
||||
Blender has many interlinking data types which have an auto-generated reference api which often has the information
|
||||
Blender has many interlinking data types which have an auto-generated reference API which often has the information
|
||||
you need to write a script, but can be difficult to use.
|
||||
|
||||
This document is designed to help you understand how to use the reference api.
|
||||
This document is designed to help you understand how to use the reference API.
|
||||
|
||||
|
||||
Reference API Scope
|
||||
===================
|
||||
|
||||
The reference API covers :mod:`bpy.types`, which stores types accessed via :mod:`bpy.context` - *The user context*
|
||||
or :mod:`bpy.data` - *Blend file data*.
|
||||
The reference API covers :mod:`bpy.types`, which stores types accessed via :mod:`bpy.context` -- *the user context*
|
||||
or :mod:`bpy.data` -- *blend-file data*.
|
||||
|
||||
Other modules such as :mod:`bmesh` and :mod:`aud` are not using Blenders data API
|
||||
Other modules such as :mod:`bmesh` and :mod:`aud` are not using Blender's data API
|
||||
so this document doesn't apply to those modules.
|
||||
|
||||
|
||||
Data Access
|
||||
===========
|
||||
|
||||
The most common case for using the reference API is to find out how to access data in the blend file.
|
||||
|
||||
Before going any further its best to be aware of ID Data-Blocks in Blender since you will often find properties
|
||||
The most common case for using the reference API is to find out how to access data in the blend-file.
|
||||
Before going any further its best to be aware of ID data-blocks in Blender since you will often find properties
|
||||
relative to them.
|
||||
|
||||
|
||||
ID Data
|
||||
-------
|
||||
|
||||
ID Data-Blocks are used in Blender as top-level data containers.
|
||||
ID data-blocks are used in Blender as top-level data containers.
|
||||
From the user interface this isn't so obvious, but when developing you need to know about ID data-blocks.
|
||||
ID data types include Scene, Group, Object, Mesh, Workspace, World, Armature, Image and Texture.
|
||||
For a full list see the subclasses of :class:`bpy.types.ID`.
|
||||
|
||||
From the user interface this isn't so obvious, but when developing you need to know about ID Data-Blocks.
|
||||
Here are some characteristics ID data-blocks share:
|
||||
|
||||
ID data types include Scene, Group, Object, Mesh, Screen, World, Armature, Image and Texture.
|
||||
for a full list see the sub-classes of :class:`bpy.types.ID`
|
||||
|
||||
Here are some characteristics ID Data-Blocks share.
|
||||
|
||||
- ID's are blend file data, so loading a new blend file reloads an entire new set of Data-Blocks.
|
||||
- ID's can be accessed in Python from ``bpy.data.*``
|
||||
- IDs are blend-file data, so loading a new blend-file reloads an entire new set of data-blocks.
|
||||
- IDs can be accessed in Python from ``bpy.data.*``.
|
||||
- Each data-block has a unique ``.name`` attribute, displayed in the interface.
|
||||
- Animation data is stored in ID's ``.animation_data``.
|
||||
- ID's are the only data types that can be linked between blend files.
|
||||
- ID's can be added/copied and removed via Python.
|
||||
- ID's have their own garbage-collection system which frees unused ID's when saving.
|
||||
- When a data-block has a reference to some external data, this is typically an ID Data-Block.
|
||||
- Animation data is stored in IDs ``.animation_data``.
|
||||
- IDs are the only data types that can be linked between blend-files.
|
||||
- IDs can be added/copied and removed via Python.
|
||||
- IDs have their own garbage-collection system which frees unused IDs when saving.
|
||||
- When a data-block has a reference to some external data, this is typically an ID data-block.
|
||||
|
||||
|
||||
Simple Data Access
|
||||
------------------
|
||||
|
||||
Lets start with a simple case, say you want a python script to adjust the object's location.
|
||||
In this simple case a Python script is used to adjust the object's location.
|
||||
Start by collecting the information where the data is located.
|
||||
|
||||
Start by finding this setting in the interface ``Properties Window -> Object -> Transform -> Location``
|
||||
|
||||
From the button you can right click and select **Online Python Reference**, this will link you to:
|
||||
:class:`bpy.types.Object.location`
|
||||
|
||||
Being an API reference, this link often gives little more information then the tool-tip, though some of the pages
|
||||
First find this setting in the interface ``Properties editor -> Object -> Transform -> Location``.
|
||||
From the button context menu select *Online Python Reference*, this will link you to:
|
||||
:class:`bpy.types.Object.location`.
|
||||
Being an API reference, this link often gives little more information then the tooltip, though some of the pages
|
||||
include examples (normally at the top of the page).
|
||||
But you now know that you have to use ``.location`` and that its an array of three floats.
|
||||
|
||||
At this point you may say *Now what?* - you know that you have to use ``.location`` and that its an array of 3 floats
|
||||
but you're still left wondering how to access this in a script.
|
||||
|
||||
So the next step is to find out where to access objects, go down to the bottom of the page to the **References**
|
||||
section, for objects there are many references, but one of the most common places to access objects is via the context.
|
||||
|
||||
It's easy to be overwhelmed at this point since there ``Object`` get referenced in so many places - modifiers,
|
||||
functions, textures and constraints.
|
||||
|
||||
So the next step is to find out where to access objects, go down to the bottom of the page to the references section,
|
||||
for objects there are many references, but one of the most common places to access objects is via the context.
|
||||
It's easy to be overwhelmed at this point since there ``Object`` get referenced in so many places:
|
||||
modifiers, functions, textures and constraints.
|
||||
But if you want to access any data the user has selected
|
||||
you typically only need to check the :mod:`bpy.context` references.
|
||||
|
||||
Even then, in this case there are quite a few though if you read over these - most are mode specific.
|
||||
If you happen to be writing a tool that only runs in weight paint mode, then using ``weight_paint_object``
|
||||
would be appropriate.
|
||||
However to access an item the user last selected, look for the ``active`` members,
|
||||
Having access to a single active member the user selects is a convention in Blender: eg. ``active_bone``,
|
||||
``active_pose_bone``, ``active_node`` ... and in this case we can use - ``active_object``.
|
||||
Even then, in this case there are quite a few though
|
||||
if you read over these you'll notice that most are mode specific.
|
||||
If you happen to be writing a tool that only runs in Weight Paint Mode,
|
||||
then using ``weight_paint_object`` would be appropriate.
|
||||
However, to access an item the user last selected, look for the ``active`` members,
|
||||
Having access to a single active member the user selects is a convention in Blender:
|
||||
e.g. ``active_bone``, ``active_pose_bone``, ``active_node``, etc. and in this case you can use ``active_object``.
|
||||
|
||||
|
||||
So now we have enough information to find the location of the active object.
|
||||
So now you have enough information to find the location of the active object.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
bpy.context.active_object.location
|
||||
|
||||
You can type this into the python console to see the result.
|
||||
|
||||
You can type this into the Python console to see the result.
|
||||
The other common place to access objects in the reference is :class:`bpy.types.BlendData.objects`.
|
||||
|
||||
.. note::
|
||||
@ -100,7 +90,7 @@ The other common place to access objects in the reference is :class:`bpy.types.B
|
||||
so the documentation points there.
|
||||
|
||||
|
||||
With :mod:`bpy.data.objects`, this is a collection of objects so you need to access one of its members.
|
||||
With :mod:`bpy.data.objects`, this is a collection of objects so you need to access one of its members:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -117,37 +107,34 @@ Here are some more complex examples:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# access a render layers samples
|
||||
bpy.context.scene.render.layers["RenderLayer"].samples
|
||||
# Access the number of samples for the Cycles render engine.
|
||||
bpy.context.scene.cycles.samples
|
||||
|
||||
# access to the current weight paint brush size
|
||||
# Access to the current weight paint brush size.
|
||||
bpy.context.tool_settings.weight_paint.brush.size
|
||||
|
||||
# check if the window is fullscreen
|
||||
# Check if the window is full-screen.
|
||||
bpy.context.window.screen.show_fullscreen
|
||||
|
||||
|
||||
As you can see there are times when you want to access data which is nested
|
||||
in a way that causes you to go through a few indirections.
|
||||
The properties are arranged to match how data is stored internally (in Blender's C code) which is often logical
|
||||
but not always quite what you would expect from using Blender.
|
||||
So this takes some time to learn, it helps you understand how data fits together in Blender
|
||||
which is important to know when writing scripts.
|
||||
|
||||
The properties are arranged to match how data is stored internally (in blenders C code) which is often logical but
|
||||
not always quite what you would expect from using Blender.
|
||||
|
||||
So this takes some time to learn, it helps you understand how data fits together in Blender which is important
|
||||
to know when writing scripts.
|
||||
|
||||
|
||||
When starting out scripting you will often run into the problem where you're not sure how to access the data you want.
|
||||
|
||||
There are a few ways to do this.
|
||||
When starting out scripting you will often run into the problem
|
||||
where you're not sure how to access the data you want.
|
||||
There are a few ways to do this:
|
||||
|
||||
- Use the Python console's auto-complete to inspect properties.
|
||||
*This can be hit-and-miss but has the advantage
|
||||
that you can easily see the values of properties and assign them to interactively see the results.*
|
||||
- Copy the Data-Path from the user interface.
|
||||
*Explained further in :ref:`Copy Data Path <info_data_path_copy>`*
|
||||
- Copy the data path from the user interface.
|
||||
*Explained further in* :ref:`Copy Data Path <info_data_path_copy>`.
|
||||
- Using the documentation to follow references.
|
||||
*Explained further in :ref:`Indirect Data Access <info_data_path_indirect>`*
|
||||
*Explained further in* :ref:`Indirect Data Access <info_data_path_indirect>`.
|
||||
|
||||
|
||||
.. _info_data_path_copy:
|
||||
@ -155,42 +142,36 @@ There are a few ways to do this.
|
||||
Copy Data Path
|
||||
--------------
|
||||
|
||||
Blender can compute the Python string to a property which is shown in the tool-tip, on the line below ``Python: ...``,
|
||||
This saves having to use the API reference to click back up the references to find where data is accessed from.
|
||||
|
||||
There is a user-interface feature to copy the data-path which gives the path from an :class:`bpy.types.ID` data-block,
|
||||
Blender can compute the Python string to a property which is shown in the tooltip,
|
||||
on the line below ``Python: ...``. This saves having to open the API references to find where data is accessed from.
|
||||
In the context menu is a copy data-path tool which gives the path from an :class:`bpy.types.ID` data-block,
|
||||
to its property.
|
||||
|
||||
To see how this works we'll get the path to the Subdivision-Surface modifiers subdivision setting.
|
||||
|
||||
Start with the default scene and select the **Modifiers** tab, then add a **Subdivision-Surface** modifier to the cube.
|
||||
|
||||
Now hover your mouse over the button labeled **View**, The tool-tip includes :class:`bpy.types.SubsurfModifier.levels`
|
||||
but we want the path from the object to this property.
|
||||
To see how this works you'll get the path to the Subdivision Surface modifiers *Levels* setting.
|
||||
Start with the default scene and select the Modifiers tab, then add a Subdivision Surface modifier to the cube.
|
||||
Now hover your mouse over the button labeled *Levels Viewport*,
|
||||
The tooltip includes :class:`bpy.types.SubsurfModifier.levels` but you want the path from the object to this property.
|
||||
|
||||
Note that the text copied won't include the ``bpy.data.collection["name"].`` component since its assumed that
|
||||
you won't be doing collection look-ups on every access and typically you'll want to use the context rather
|
||||
then access each :class:`bpy.types.ID` instance by name.
|
||||
|
||||
|
||||
Type in the ID path into a Python console :mod:`bpy.context.active_object`.
|
||||
Include the trailing dot and don't hit "enter", yet.
|
||||
Include the trailing dot and don't execute the code, yet.
|
||||
|
||||
Now right-click on the button and select **Copy Data Path**, then paste the result into the console.
|
||||
|
||||
So now you should have the answer:
|
||||
Now in the button's context menu select *Copy Data Path*, then paste the result into the console:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
bpy.context.active_object.modifiers["Subsurf"].levels
|
||||
|
||||
Hit "enter" and you'll get the current value of 1. Now try changing the value to 2:
|
||||
Press :kbd:`Return` and you'll get the current value of 1. Now try changing the value to 2:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
bpy.context.active_object.modifiers["Subsurf"].levels = 2
|
||||
bpy.context.active_object.modifiers["Subsurf"].levels = 2
|
||||
|
||||
You can see the value update in the Subdivision-Surface modifier's UI as well as the cube.
|
||||
You can see the value update in the Subdivision Surface modifier's UI as well as the cube.
|
||||
|
||||
|
||||
.. _info_data_path_indirect:
|
||||
@ -198,51 +179,45 @@ You can see the value update in the Subdivision-Surface modifier's UI as well as
|
||||
Indirect Data Access
|
||||
--------------------
|
||||
|
||||
For this example we'll go over something more involved, showing the steps to access the active sculpt brushes texture.
|
||||
This more advanced example shows the steps to access the active sculpt brushes texture.
|
||||
For example, if you want to access the texture of a brush via Python to adjust its ``contrast``.
|
||||
|
||||
Lets say we want to access the texture of a brush via Python, to adjust its ``contrast`` for example.
|
||||
#. Start in the default scene and enable Sculpt Mode from the 3D Viewport header.
|
||||
#. From the Sidebar expand the Brush Settings panel's *Texture* subpanel and add a new texture.
|
||||
*Notice the texture data-block menu itself doesn't have very useful links (you can check the tooltips).*
|
||||
#. The contrast setting isn't exposed in the Sidebar, so view the texture in the properties editor:
|
||||
|
||||
- Start in the default scene and enable 'Sculpt' mode from the 3D-View header.
|
||||
- From the toolbar expand the **Texture** panel and add a new texture.
|
||||
*Notice the texture button its self doesn't have very useful links (you can check the tooltips).*
|
||||
- The contrast setting isn't exposed in the sculpt toolbar, so view the texture in the properties panel...
|
||||
- In the properties editor select the Texture tab.
|
||||
- Select brush texture.
|
||||
- Expand the *Colors* panel to locate the *Contrast* number field.
|
||||
#. Open the context menu of the contrast field and select *Online Python Reference*.
|
||||
This takes you to ``bpy.types.Texture.contrast``. Now you can see that ``contrast`` is a property of texture.
|
||||
#. To find out how to access the texture from the brush check on the references at the bottom of the page.
|
||||
Sometimes there are many references, and it may take some guesswork to find the right one,
|
||||
but in this case it's ``Brush.texture``.
|
||||
|
||||
- In the properties button select the Texture context.
|
||||
- Select the Brush icon to show the brush texture.
|
||||
- Expand the *Colors* panel to locate the *Contrast* button.
|
||||
- Right click on the contrast button and select **Online Python Reference**
|
||||
This takes you to ``bpy.types.Texture.contrast``
|
||||
- Now we can see that ``contrast`` is a property of texture,
|
||||
so next we'll check on how to access the texture from the brush.
|
||||
- Check on the **References** at the bottom of the page, sometimes there are many references, and it may take
|
||||
some guess work to find the right one, but in this case its obviously ``Brush.texture``.
|
||||
#. Now you know that the texture can be accessed from ``bpy.data.brushes["BrushName"].texture``
|
||||
but normally you *won't* want to access the brush by name, instead you want to access the active brush.
|
||||
So the next step is to check on where brushes are accessed from via the references.
|
||||
In this case there it is simply ``bpy.context.brush``.
|
||||
|
||||
*Now we know that the texture can be accessed from* ``bpy.data.brushes["BrushName"].texture``
|
||||
*but normally you won't want to access the brush by name, so we'll see now to access the active brush instead.*
|
||||
- So the next step is to check on where brushes are accessed from via the **References**.
|
||||
In this case there is simply ``bpy.context.brush`` which is all we need.
|
||||
Now you can use the Python console to form the nested properties needed to access brush textures contrast:
|
||||
*Context -> Brush -> Texture -> Contrast*.
|
||||
|
||||
Now you can use the Python console to form the nested properties needed to access brush textures contrast,
|
||||
logically we now know.
|
||||
|
||||
*Context -> Brush -> Texture -> Contrast*
|
||||
|
||||
Since the attribute for each is given along the way we can compose the data path in the python console:
|
||||
Since the attribute for each is given along the way you can compose the data path in the Python console:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
bpy.context.brush.texture.contrast
|
||||
|
||||
|
||||
There can be multiple ways to access the same data, which you choose often depends on the task.
|
||||
|
||||
An alternate path to access the same setting is...
|
||||
An alternate path to access the same setting is:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
bpy.context.sculpt.brush.texture.contrast
|
||||
|
||||
Or access the brush directly...
|
||||
Or access the brush directly:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -251,27 +226,24 @@ Or access the brush directly...
|
||||
|
||||
If you are writing a user tool normally you want to use the :mod:`bpy.context` since the user normally expects
|
||||
the tool to operate on what they have selected.
|
||||
|
||||
For automation you are more likely to use :mod:`bpy.data` since you want to be able to access specific data and manipulate
|
||||
it, no matter what the user currently has the view set at.
|
||||
For automation you are more likely to use :mod:`bpy.data` since you want to be able to access specific data and
|
||||
manipulate it, no matter what the user currently has the view set at.
|
||||
|
||||
|
||||
Operators
|
||||
=========
|
||||
|
||||
Most key-strokes and buttons in Blender call an operator which is also exposed to python via :mod:`bpy.ops`,
|
||||
Most hotkeys and buttons in Blender call an operator which is also exposed to Python via :mod:`bpy.ops`.
|
||||
|
||||
To see the Python equivalent hover your mouse over the button and see the tool-tip,
|
||||
eg ``Python: bpy.ops.render.render()``,
|
||||
If there is no tool-tip or the ``Python:`` line is missing then this button is not using an operator and
|
||||
can't be accessed from Python.
|
||||
To see the Python equivalent hover your mouse over the button and see the tooltip,
|
||||
e.g ``Python: bpy.ops.render.render()``,
|
||||
If there is no tooltip or the ``Python:`` line is missing then this button is not using an operator
|
||||
and can't be accessed from Python.
|
||||
|
||||
|
||||
If you want to use this in a script you can press :kbd:`Control-C` while your mouse is over the button to copy it to the
|
||||
clipboard.
|
||||
|
||||
You can also right click on the button and view the **Online Python Reference**, this mainly shows arguments and
|
||||
their defaults however operators written in Python show their file and line number which may be useful if you
|
||||
If you want to use this in a script you can press :kbd:`Ctrl-C` while your mouse is over the button
|
||||
to copy it to the clipboard.
|
||||
You can also use button's context menu and view the *Online Python Reference*, this mainly shows arguments and
|
||||
their defaults, however, operators written in Python show their file and line number which may be useful if you
|
||||
are interested to check on the source code.
|
||||
|
||||
.. note::
|
||||
@ -280,21 +252,18 @@ are interested to check on the source code.
|
||||
for more on this see :ref:`using operators <using_operators>`.
|
||||
|
||||
|
||||
Info View
|
||||
---------
|
||||
Info Editor
|
||||
-----------
|
||||
|
||||
Blender records operators you run and displays them in the **Info** space.
|
||||
This is located above the file-menu which can be dragged down to display its contents.
|
||||
Blender records operators you run and displays them in the Info editor.
|
||||
Select the Scripting workspace that comes default with Blender to see its output.
|
||||
You can perform some actions and see them show up -- delete a vertex for example.
|
||||
|
||||
Select the **Script** screen that comes default with Blender to see its output.
|
||||
You can perform some actions and see them show up - delete a vertex for example.
|
||||
|
||||
Each entry can be selected (Right-Mouse-Button),
|
||||
then copied :kbd:`Control-C`, usually to paste in the text editor or python console.
|
||||
Each entry can be selected, then copied :kbd:`Ctrl-C`, usually to paste in the text editor or Python console.
|
||||
|
||||
.. note::
|
||||
|
||||
Not all operators get registered for display,
|
||||
zooming the view for example isn't so useful to repeat so its excluded from the output.
|
||||
|
||||
To display *every* operator that runs see :ref:`Show All Operators <info_show_all_operators>`
|
||||
To display *every* operator that runs see :ref:`Show All Operators <info_show_all_operators>`.
|
||||
|
@ -3,38 +3,34 @@
|
||||
Best Practice
|
||||
*************
|
||||
|
||||
When writing your own scripts python is great for new developers to pick up and become productive,
|
||||
but you can also pick up odd habits or at least write scripts that are not easy for others to understand.
|
||||
|
||||
When writing your own scripts Python is great for new developers to pick up and become productive,
|
||||
but you can also pick up bad practices or at least write scripts that are not easy for others to understand.
|
||||
For your own work this is of course fine,
|
||||
but if you want to collaborate with others or have your work included with blender there are practices we encourage.
|
||||
but if you want to collaborate with others or have your work included with Blender there are practices we encourage.
|
||||
|
||||
|
||||
Style Conventions
|
||||
=================
|
||||
|
||||
For Blender/Python development we have chosen to follow python suggested style guide to avoid mixing styles
|
||||
amongst our own scripts and make it easier to use python scripts from other projects.
|
||||
For Blender Python development we have chosen to follow Python suggested style guide to avoid mixing styles
|
||||
among our own scripts and make it easier to use Python scripts from other projects.
|
||||
Using our style guide for your own scripts makes it easier if you eventually want to contribute them to Blender.
|
||||
|
||||
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 <https://www.python.org/dev/peps/pep-0008/>`__
|
||||
and here is a brief listing of pep8 criteria:
|
||||
|
||||
This style guide is known as pep8 and can be found `here <https://www.python.org/dev/peps/pep-0008/>`_
|
||||
- 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 wildcard importing ``*``)
|
||||
- Don't use multiple statements on a single line: ``if val: body``, separate onto two lines instead.
|
||||
|
||||
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.
|
||||
As well as pep8 we have additional 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.
|
||||
Both are of course strings, but in our internal API enums are unique items from a limited set, e.g:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -42,14 +38,14 @@ As well as pep8 we have other conventions used for blender python scripts.
|
||||
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.
|
||||
we have decided that this is too restrictive so it 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.
|
||||
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.
|
||||
To enable line length checks use this instead:
|
||||
|
||||
``# <pep8-80 compliant>``
|
||||
|
||||
@ -59,85 +55,79 @@ User Interface Layout
|
||||
|
||||
Some notes to keep in mind when writing UI layouts:
|
||||
|
||||
- UI code is quite simple. Layout declarations are there to easily create a decent layout.
|
||||
UI code is quite simple. Layout declarations are there to easily create a decent layout.
|
||||
The general rule here is: If you need more code for the layout declaration,
|
||||
than for the actual properties, then you are doing it wrong.
|
||||
|
||||
General rule here: If you need more code for the layout declaration,
|
||||
then for the actual properties, you do it wrong.
|
||||
|
||||
Example layouts:
|
||||
.. rubric:: Example layouts:
|
||||
|
||||
- layout()
|
||||
``layout()``
|
||||
The basic layout is a simple top-to-bottom layout.
|
||||
|
||||
The basic layout is a simple Top -> Bottom layout.
|
||||
.. code-block:: python
|
||||
|
||||
.. code-block:: python
|
||||
layout.prop()
|
||||
layout.prop()
|
||||
|
||||
layout.prop()
|
||||
layout.prop()
|
||||
``layout.row()``
|
||||
Use ``row()``, when you want more than one property in a single line.
|
||||
|
||||
- layout.row()
|
||||
.. code-block:: python
|
||||
|
||||
Use row(), when you want more than 1 property in one line.
|
||||
row = layout.row()
|
||||
row.prop()
|
||||
row.prop()
|
||||
|
||||
.. code-block:: python
|
||||
``layout.column()``
|
||||
Use ``column()``, when you want your properties in a column.
|
||||
|
||||
row = layout.row()
|
||||
row.prop()
|
||||
row.prop()
|
||||
.. code-block:: python
|
||||
|
||||
- layout.column()
|
||||
col = layout.column()
|
||||
col.prop()
|
||||
col.prop()
|
||||
|
||||
Use column(), when you want your properties in a column.
|
||||
``layout.split()``
|
||||
This can be used to create more complex layouts.
|
||||
For example, you can split the layout and create two ``column()`` layouts next to each other.
|
||||
Do not use split, when you simply want two properties in a row. Use ``row()`` instead.
|
||||
|
||||
.. code-block:: python
|
||||
.. code-block:: python
|
||||
|
||||
col = layout.column()
|
||||
col.prop()
|
||||
col.prop()
|
||||
split = layout.split()
|
||||
|
||||
- layout.split()
|
||||
col = split.column()
|
||||
col.prop()
|
||||
col.prop()
|
||||
|
||||
This can be used to create more complex layouts.
|
||||
For example you can split the layout and create two column() layouts next to each other.
|
||||
Don't use split, when you simply want two properties in a row. Use row() for that.
|
||||
col = split.column()
|
||||
col.prop()
|
||||
col.prop()
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
split = layout.split()
|
||||
|
||||
col = split.column()
|
||||
col.prop()
|
||||
col.prop()
|
||||
|
||||
col = split.column()
|
||||
col.prop()
|
||||
col.prop()
|
||||
|
||||
Declaration names:
|
||||
.. rubric:: Declaration names:
|
||||
|
||||
Try to only use these variable names for layout declarations:
|
||||
|
||||
- row for a row() layout
|
||||
- col for a column() layout
|
||||
- split for a split() layout
|
||||
- flow for a column_flow() layout
|
||||
- sub for a sub layout (a column inside a column for example)
|
||||
:row: for a ``row()`` layout
|
||||
:col: for a ``column()`` layout
|
||||
:split: for a ``split()`` layout
|
||||
:flow: for a ``column_flow()`` layout
|
||||
:sub: for a sub layout (a column inside a column for example)
|
||||
|
||||
|
||||
Script Efficiency
|
||||
=================
|
||||
|
||||
|
||||
List Manipulation (General Python Tips)
|
||||
---------------------------------------
|
||||
|
||||
|
||||
Searching for list items
|
||||
Searching for List Items
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
In Python there are some handy list functions that save you having to search through the list.
|
||||
|
||||
Even though you are not looping on the list data **python is**,
|
||||
Even though you are not looping on the list data **Python is**,
|
||||
so you need to be aware of functions that will slow down your script by searching the whole list.
|
||||
|
||||
.. code-block:: python
|
||||
@ -150,23 +140,21 @@ so you need to be aware of functions that will slow down your script by searchin
|
||||
|
||||
Modifying Lists
|
||||
^^^^^^^^^^^^^^^
|
||||
In python we can add and remove from a list, this is slower when the list length is modified,
|
||||
especially at the start of the list, since all the data after the index of
|
||||
modification needs to be moved up or down 1 place.
|
||||
|
||||
The most simple way to add onto the end of the list is to use
|
||||
``my_list.append(list_item)`` or ``my_list.extend(some_list)`` and the fastest way to
|
||||
remove an item is ``my_list.pop()`` or ``del my_list[-1]``.
|
||||
In Python you can add and remove from a list, this is slower when the list length is modified,
|
||||
especially at the start of the list, since all the data after the index of
|
||||
modification needs to be moved up or down one place.
|
||||
|
||||
The fastest way to add onto the end of the list is to use
|
||||
``my_list.append(list_item)`` or ``my_list.extend(some_list)`` and
|
||||
to remove an item is ``my_list.pop()`` or ``del my_list[-1]``.
|
||||
|
||||
To use an index you can use ``my_list.insert(index, list_item)`` or ``list.pop(index)``
|
||||
for list removal, but these are slower.
|
||||
|
||||
Sometimes its faster (but more memory hungry) to just rebuild the list.
|
||||
|
||||
|
||||
Say you want to remove all triangular polygons in a list.
|
||||
|
||||
Rather than...
|
||||
Sometimes it's faster (but less memory efficient) to just rebuild the list.
|
||||
For example if you want to remove all triangular polygons in a list.
|
||||
Rather than:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -179,7 +167,7 @@ Rather than...
|
||||
polygons.pop(p_idx) # remove the triangle
|
||||
|
||||
|
||||
It's faster to build a new list with list comprehension.
|
||||
It's faster to build a new list with list comprehension:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -189,14 +177,14 @@ It's faster to build a new list with list comprehension.
|
||||
Adding List Items
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
If you have a list that you want to add onto another list, rather than...
|
||||
If you have a list that you want to add onto another list, rather than:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
for l in some_list:
|
||||
my_list.append(l)
|
||||
|
||||
Use...
|
||||
Use:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -205,9 +193,7 @@ Use...
|
||||
|
||||
Note that insert can be used when needed,
|
||||
but it is slower than append especially when inserting at the start of a long list.
|
||||
|
||||
This example shows a very sub-optimal way of making a reversed list.
|
||||
|
||||
This example shows a very suboptimal way of making a reversed list:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -219,7 +205,6 @@ This example shows a very sub-optimal way of making a reversed list.
|
||||
Python provides more convenient ways to reverse a list using the slice method,
|
||||
but you may want to time this before relying on it too much:
|
||||
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
some_reversed_list = some_list[::-1]
|
||||
@ -228,12 +213,10 @@ but you may want to time this before relying on it too much:
|
||||
Removing List Items
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Use ``my_list.pop(index)`` rather than ``my_list.remove(list_item)``
|
||||
|
||||
Use ``my_list.pop(index)`` rather than ``my_list.remove(list_item)``.
|
||||
This requires you to have the index of the list item but is faster since ``remove()`` will search the list.
|
||||
|
||||
Here is an example of how to remove items in 1 loop,
|
||||
removing the last items first, which is faster (as explained above).
|
||||
Here is an example of how to remove items in one loop,
|
||||
removing the last items first, which is faster (as explained above):
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -247,7 +230,7 @@ removing the last items first, which is faster (as explained above).
|
||||
|
||||
This example shows a fast way of removing items,
|
||||
for use in cases where you can alter the list order without breaking the scripts functionality.
|
||||
This works by swapping 2 list items, so the item you remove is always last.
|
||||
This works by swapping two list items, so the item you remove is always last:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -260,64 +243,59 @@ This works by swapping 2 list items, so the item you remove is always last.
|
||||
my_list.pop()
|
||||
|
||||
|
||||
When removing many items in a large list this can provide a good speedup.
|
||||
When removing many items in a large list this can provide a good speed-up.
|
||||
|
||||
|
||||
Avoid Copying Lists
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
When passing a list/dictionary to a function,
|
||||
When passing a list or dictionary to a function,
|
||||
it is faster to have the function modify the list rather than returning
|
||||
a new list so python doesn't have to duplicate the list in memory.
|
||||
a new list so Python doesn't have to duplicate the list in memory.
|
||||
|
||||
Functions that modify a list in-place are more efficient than functions that create new lists.
|
||||
|
||||
|
||||
This is generally slower so only use for functions when it makes sense not to modify the list in place.
|
||||
This is generally slower so only use for functions when it makes sense not to modify the list in place:
|
||||
|
||||
>>> my_list = some_list_func(my_list)
|
||||
|
||||
|
||||
This is generally faster since there is no re-assignment and no list duplication.
|
||||
This is generally faster since there is no re-assignment and no list duplication:
|
||||
|
||||
>>> some_list_func(vec)
|
||||
|
||||
|
||||
Also note that passing a sliced list makes a copy of the list in python memory.
|
||||
Also note that, passing a sliced list makes a copy of the list in Python memory:
|
||||
|
||||
>>> foobar(my_list[:])
|
||||
|
||||
If my_list was a large array containing 10000's of items, a copy could use a lot of extra memory.
|
||||
If my_list was a large array containing 10,000's of items, a copy could use a lot of extra memory.
|
||||
|
||||
|
||||
Writing Strings to a File (Python General)
|
||||
------------------------------------------
|
||||
|
||||
Here are 3 ways of joining multiple strings into one string for writing.
|
||||
This also applies to any area of your code that involves a lot of string joining.
|
||||
Here are three ways of joining multiple strings into one string for writing.
|
||||
This also applies to any area of your code that involves a lot of string joining:
|
||||
|
||||
String concatenation
|
||||
This is the slowest option, do **not** use if you can avoid it, especially when writing data in a loop.
|
||||
|
||||
>>> file.write(str1 + " " + str2 + " " + str3 + "\n")
|
||||
|
||||
String formatting
|
||||
Use this when you are writing string data from floats and ints.
|
||||
|
||||
>>> file.write("%s %s %s\n" % (str1, str2, str3))
|
||||
|
||||
String joining
|
||||
Use to join a list of strings (the list may be temporary). In the following example, the strings are joined with
|
||||
a space " " in between, other examples are "" or ", ".
|
||||
|
||||
>>> file.write(" ".join((str1, str2, str3, "\n")))
|
||||
|
||||
|
||||
``String addition`` -
|
||||
this is the slowest option, *don't use if you can help it, especially when writing data in a loop*.
|
||||
|
||||
>>> file.write(str1 + " " + str2 + " " + str3 + "\n")
|
||||
|
||||
|
||||
``String formatting`` -
|
||||
use this when you are writing string data from floats and ints.
|
||||
|
||||
>>> file.write("%s %s %s\n" % (str1, str2, str3))
|
||||
|
||||
|
||||
``String join() function``
|
||||
use to join a list of strings (the list may be temporary). In the following example, the strings are joined with a space " " in between, other examples are "" or ", ".
|
||||
|
||||
>>> file.write(" ".join([str1, str2, str3, "\n"]))
|
||||
|
||||
|
||||
Join is fastest on many strings,
|
||||
`string formatting <https://wiki.blender.org/index.php/Dev:Source/Modeling/BMesh/Design>`__
|
||||
is quite fast too (better for converting data types). String arithmetic is slowest.
|
||||
Join is fastest on many strings, string formatting is quite fast too (better for converting data types).
|
||||
String concatenation is the slowest.
|
||||
|
||||
|
||||
Parsing Strings (Import/Exporting)
|
||||
@ -333,36 +311,35 @@ Parsing Numbers
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
Use ``float(string)`` rather than ``eval(string)``, if you know the value will be an int then ``int(string)``,
|
||||
float() will work for an int too but it is faster to read ints with int().
|
||||
``float()`` will work for an int too but it is faster to read ints with ``int()``.
|
||||
|
||||
|
||||
Checking String Start/End
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If you are checking the start of a string for a keyword, rather than...
|
||||
If you are checking the start of a string for a keyword, rather than:
|
||||
|
||||
>>> if line[0:5] == "vert ": ...
|
||||
|
||||
use...
|
||||
Use:
|
||||
|
||||
>>> if line.startswith("vert "):
|
||||
|
||||
Using ``startswith()`` is slightly faster (approx 5%) and also avoids a possible
|
||||
error with the slice length not matching the string length.
|
||||
Using ``startswith()`` is slightly faster (around 5%) and also avoids a possible error
|
||||
with the slice length not matching the string length.
|
||||
|
||||
my_string.endswith("foo_bar") can be used for line endings too.
|
||||
``my_string.endswith("foo_bar")`` can be used for line endings too.
|
||||
|
||||
If you are unsure whether the text is upper or lower case, use the ``lower()`` or ``upper()`` string function.
|
||||
If you are unsure whether the text is upper or lower case, use the ``lower()`` or ``upper()`` string function:
|
||||
|
||||
>>> if line.lower().startswith("vert ")
|
||||
|
||||
|
||||
Use try/except Sparingly
|
||||
------------------------
|
||||
Error Handling
|
||||
--------------
|
||||
|
||||
The **try** statement is useful to save time writing error checking code.
|
||||
|
||||
However **try** is significantly slower than an **if** since an exception has to be set each time,
|
||||
However, **try** is significantly slower than an **if** since an exception has to be set each time,
|
||||
so avoid using **try** in areas of your code that execute in a loop and runs many times.
|
||||
|
||||
There are cases where using **try** is faster than checking whether the condition will raise an error,
|
||||
@ -382,7 +359,7 @@ In cases where you know you are checking for the same value which is referenced
|
||||
Time Your Code
|
||||
--------------
|
||||
|
||||
While developing a script it is good to time it to be aware of any changes in performance, this can be done simply.
|
||||
While developing a script it is good to time it to be aware of any changes in performance, this can be done simply:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -391,4 +368,4 @@ While developing a script it is good to time it to be aware of any changes in pe
|
||||
|
||||
# do something...
|
||||
|
||||
print("My Script Finished: %.4f sec" % (time.time() - time_start))
|
||||
print("My Script Finished: {:.4f} sec".format(time.time() - time_start))
|
||||
|
@ -4,7 +4,7 @@ Gotchas
|
||||
*******
|
||||
|
||||
This document attempts to help you work with the Blender API in areas
|
||||
that can be troublesome and avoid practices that are known to give instability.
|
||||
that can be troublesome and avoid practices that are known to cause instability.
|
||||
|
||||
|
||||
.. _using_operators:
|
||||
@ -12,13 +12,13 @@ that can be troublesome and avoid practices that are known to give instability.
|
||||
Using Operators
|
||||
===============
|
||||
|
||||
Blender's operators are tools for users to access, that Python can access them too is very useful
|
||||
nevertheless operators have limitations that can make them cumbersome to script.
|
||||
Blender's operators are tools for users to access, that can access with Python too which is very useful.
|
||||
Still operators have limitations that can make them cumbersome to script.
|
||||
|
||||
Main limits are...
|
||||
The main limits are:
|
||||
|
||||
- Can't pass data such as objects, meshes or materials to operate on (operators use the context instead)
|
||||
- The return value from calling an operator gives the success (if it finished or was canceled),
|
||||
- Can't pass data such as objects, meshes or materials to operate on (operators use the context instead).
|
||||
- The return value from calling an operator is the success (if it finished or was canceled),
|
||||
in some cases it would be more logical from an API perspective to return the result of the operation.
|
||||
- Operators poll function can fail where an API function would raise an exception giving details on exactly why.
|
||||
|
||||
@ -34,26 +34,23 @@ When calling an operator gives an error like this:
|
||||
Which raises the question as to what the correct context might be?
|
||||
|
||||
Typically operators check for the active area type, a selection or active object they can operate on,
|
||||
but some operators are more picky about when they run.
|
||||
|
||||
but some operators are more strict when they run.
|
||||
In most cases you can figure out what context an operator needs
|
||||
simply be seeing how it's used in Blender and thinking about what it does.
|
||||
|
||||
Unfortunately if you're still stuck - the only way to **really** know
|
||||
what's going on is to read the source code for the poll function and see what its checking.
|
||||
by examining how it's used in Blender and thinking about what it does.
|
||||
|
||||
If you're still stuck, unfortunately, the only way to eventually know what is causing the error is
|
||||
to read the source code for the poll function and see what it is checking.
|
||||
For Python operators it's not so hard to find the source
|
||||
since it's included with Blender and the source file/line is included in the operator reference docs.
|
||||
|
||||
since it's included with Blender and the source file and line is included in the operator reference docs.
|
||||
Downloading and searching the C code isn't so simple,
|
||||
especially if you're not familiar with the C language but by searching the
|
||||
operator name or description you should be able to find the poll function with no knowledge of C.
|
||||
especially if you're not familiar with the C language but by searching the operator name or description
|
||||
you should be able to find the poll function with no knowledge of C.
|
||||
|
||||
.. note::
|
||||
|
||||
Blender does have the functionality for poll functions to describe why they fail,
|
||||
but its currently not used much, if you're interested to help improve our API
|
||||
feel free to add calls to ``CTX_wm_operator_poll_msg_set`` where its not obvious why poll fails.
|
||||
but its currently not used much, if you're interested to help improve the API
|
||||
feel free to add calls to ``CTX_wm_operator_poll_msg_set`` where its not obvious why poll fails, e.g:
|
||||
|
||||
>>> bpy.ops.gpencil.draw()
|
||||
RuntimeError: Operator bpy.ops.gpencil.draw.poll() Failed to find Grease Pencil data to draw into
|
||||
@ -63,7 +60,7 @@ The operator still doesn't work!
|
||||
--------------------------------
|
||||
|
||||
Certain operators in Blender are only intended for use in a specific context,
|
||||
some operators for example are only called from the properties window where they check the current material,
|
||||
some operators for example are only called from the properties editor where they check the current material,
|
||||
modifier or constraint.
|
||||
|
||||
Examples of this are:
|
||||
@ -74,8 +71,8 @@ Examples of this are:
|
||||
- :mod:`bpy.ops.buttons.file_browse`
|
||||
|
||||
Another possibility is that you are the first person to attempt to use this operator
|
||||
in a script and some modifications need to be made to the operator to run in a different context,
|
||||
if the operator should logically be able to run but fails when accessed from a script
|
||||
in a script and some modifications need to be made to the operator to run in a different context.
|
||||
If the operator should logically be able to run but fails when accessed from a script
|
||||
it should be reported to the bug tracker.
|
||||
|
||||
|
||||
@ -85,22 +82,20 @@ Stale Data
|
||||
No updates after setting values
|
||||
-------------------------------
|
||||
|
||||
Sometimes you want to modify values from Python and immediately access the updated values, eg:
|
||||
|
||||
Sometimes you want to modify values from Python and immediately access the updated values, e.g:
|
||||
Once changing the objects :class:`bpy.types.Object.location`
|
||||
you may want to access its transformation right after from :class:`bpy.types.Object.matrix_world`,
|
||||
but this doesn't work as you might expect.
|
||||
|
||||
Consider the calculations that might go into working out the object's final transformation, this includes:
|
||||
Consider the calculations that might contribute to the object's final transformation, this includes:
|
||||
|
||||
- animation function curves.
|
||||
- drivers and their Python expressions.
|
||||
- constraints
|
||||
- parent objects and all of their f-curves, constraints etc.
|
||||
- Animation function curves.
|
||||
- Drivers and their Python expressions.
|
||||
- Constraints
|
||||
- Parent objects and all of their F-curves, constraints, etc.
|
||||
|
||||
To avoid expensive recalculations every time a property is modified,
|
||||
Blender defers making the actual calculations until they are needed.
|
||||
|
||||
Blender defers the evaluation until the results are needed.
|
||||
However, while the script runs you may want to access the updated values.
|
||||
In this case you need to call :class:`bpy.types.ViewLayer.update` after modifying values, for example:
|
||||
|
||||
@ -110,44 +105,41 @@ In this case you need to call :class:`bpy.types.ViewLayer.update` after modifyin
|
||||
bpy.context.view_layer.update()
|
||||
|
||||
|
||||
Now all dependent data (child objects, modifiers, drivers... etc)
|
||||
Now all dependent data (child objects, modifiers, drivers, etc.)
|
||||
has been recalculated and is available to the script within active view layer.
|
||||
|
||||
|
||||
Can I redraw during the script?
|
||||
-------------------------------
|
||||
Can I redraw during script execution?
|
||||
-------------------------------------
|
||||
|
||||
The official answer to this is no, or... *"You don't want to do that"*.
|
||||
|
||||
To give some background on the topic...
|
||||
To give some background on the topic:
|
||||
|
||||
While a script executes Blender waits for it to finish and is effectively locked until its done,
|
||||
while in this state Blender won't redraw or respond to user input.
|
||||
Normally this is not such a problem because scripts distributed with Blender
|
||||
tend not to run for an extended period of time,
|
||||
nevertheless scripts *can* take ages to execute and its nice to see what's going on in the view port.
|
||||
nevertheless scripts *can* take a long time to complete and it would be nice to see progress in the viewport.
|
||||
|
||||
Tools that lock Blender in a loop and redraw are highly discouraged
|
||||
since they conflict with Blenders ability to run multiple operators
|
||||
When tools lock Blender in a loop redraw are highly discouraged
|
||||
since they conflict with Blender's ability to run multiple operators
|
||||
at once and update different parts of the interface as the tool runs.
|
||||
|
||||
So the solution here is to write a **modal** operator, that is - an operator which defines a modal() function,
|
||||
See the modal operator template in the text editor.
|
||||
|
||||
So the solution here is to write a **modal** operator, which is an operator that defines a ``modal()`` function,
|
||||
See the modal operator template in the text editor.
|
||||
Modal operators execute on user input or setup their own timers to run frequently,
|
||||
they can handle the events or pass through to be handled by the keymap or other modal operators.
|
||||
|
||||
Transform, Painting, Fly-Mode and File-Select are example of a modal operators.
|
||||
Examples of a modal operators are Transform, Painting, Fly Navigation and File Select.
|
||||
|
||||
Writing modal operators takes more effort than a simple ``for`` loop
|
||||
that happens to redraw but is more flexible and integrates better with Blenders design.
|
||||
that contains draw calls but is more flexible and integrates better with Blender's design.
|
||||
|
||||
|
||||
**Ok, Ok! I still want to draw from Python**
|
||||
.. rubric:: Ok, Ok! I still want to draw from Python
|
||||
|
||||
If you insist - yes its possible, but scripts that use this hack won't be considered
|
||||
for inclusion in Blender and any issues with using it won't be considered bugs,
|
||||
this is also not guaranteed to work in future releases.
|
||||
If you insist -- yes it's possible, but scripts that use this hack will not be considered
|
||||
for inclusion in Blender and any issue with using it will not be considered a bug,
|
||||
there is also no guaranteed compatibility in future releases.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -157,18 +149,18 @@ this is also not guaranteed to work in future releases.
|
||||
Modes and Mesh Access
|
||||
=====================
|
||||
|
||||
When working with mesh data you may run into the problem where a script fails to run as expected in edit-mode.
|
||||
This is caused by edit-mode having its own data which is only written back to the mesh when exiting edit-mode.
|
||||
When working with mesh data you may run into the problem where a script fails to run as expected in Edit-Mode.
|
||||
This is caused by Edit-Mode having its own data which is only written back to the mesh when exiting Edit-Mode.
|
||||
|
||||
A common example is that exporters may access a mesh through ``obj.data`` (a :class:`bpy.types.Mesh`)
|
||||
but the user is in edit-mode, where the mesh data is available but out of sync with the edit mesh.
|
||||
when the user is in Edit-Mode, where the mesh data is available but out of sync with the edit mesh.
|
||||
|
||||
In this situation you can...
|
||||
|
||||
- Exit edit-mode before running the tool.
|
||||
- Exit Edit-Mode before running the tool.
|
||||
- Explicitly update the mesh by calling :class:`bmesh.types.BMesh.to_mesh`.
|
||||
- Modify the script to support working on the edit-mode data directly, see: :mod:`bmesh.from_edit_mesh`.
|
||||
- Report the context as incorrect and only allow the script to run outside edit-mode.
|
||||
- Report the context as incorrect and only allow the script to run outside Edit-Mode.
|
||||
|
||||
|
||||
.. _info_gotcha_mesh_faces:
|
||||
@ -176,24 +168,24 @@ In this situation you can...
|
||||
N-Gons and Tessellation
|
||||
=======================
|
||||
|
||||
Since 2.63 NGons are supported, this adds some complexity
|
||||
Since 2.63 n-gons are supported, this adds some complexity
|
||||
since in some cases you need to access triangles still (some exporters for example).
|
||||
|
||||
There are now 3 ways to access faces:
|
||||
There are now three ways to access faces:
|
||||
|
||||
- :class:`bpy.types.MeshPolygon` -
|
||||
this is the data structure which now stores faces in object mode
|
||||
- :class:`bpy.types.MeshPolygon` --
|
||||
this is the data structure which now stores faces in Object-Mode
|
||||
(access as ``mesh.polygons`` rather than ``mesh.faces``).
|
||||
- :class:`bpy.types.MeshLoopTriangle` -
|
||||
- :class:`bpy.types.MeshLoopTriangle` --
|
||||
the result of tessellating polygons into triangles
|
||||
(access as ``mesh.loop_triangles``).
|
||||
- :class:`bmesh.types.BMFace` -
|
||||
the polygons as used in editmode.
|
||||
- :class:`bmesh.types.BMFace` --
|
||||
the polygons as used in Edit-Mode.
|
||||
|
||||
For the purpose of the following documentation,
|
||||
these will be referred to as polygons, loop triangles and bmesh-faces respectively.
|
||||
these will be referred to as polygons, loop triangles and BMesh-faces respectively.
|
||||
|
||||
5+ sided faces will be referred to as ``ngons``.
|
||||
Faces with five or more sides will be referred to as ``ngons``.
|
||||
|
||||
|
||||
Support Overview
|
||||
@ -216,58 +208,58 @@ Support Overview
|
||||
- Unusable *(read-only)*.
|
||||
- Best
|
||||
* - Export/Output
|
||||
- Good *(ngon support)*
|
||||
- Good *(When ngons can't be used)*
|
||||
- Good *(ngons, extra memory overhead)*
|
||||
- Good *(n-gon support)*
|
||||
- Good *(When n-gons cannot be used)*
|
||||
- Good *(n-gons, extra memory overhead)*
|
||||
|
||||
.. note::
|
||||
|
||||
Using the :mod:`bmesh` API is completely separate API from :mod:`bpy`,
|
||||
typically you would would use one or the other based on the level of editing needed,
|
||||
typically you would use one or the other based on the level of editing needed,
|
||||
not simply for a different way to access faces.
|
||||
|
||||
|
||||
Creating
|
||||
--------
|
||||
|
||||
All 3 datatypes can be used for face creation.
|
||||
All three data types can be used for face creation:
|
||||
|
||||
- polygons are the most efficient way to create faces but the data structure is _very_ rigid and inflexible,
|
||||
- Polygons are the most efficient way to create faces but the data structure is *very* rigid and inflexible,
|
||||
you must have all your vertices and faces ready and create them all at once.
|
||||
This is further complicated by the fact that each polygon does not store its own verts,
|
||||
This is further complicated by the fact that each polygon does not store its own vertices,
|
||||
rather they reference an index and size in :class:`bpy.types.Mesh.loops` which are a fixed array too.
|
||||
- bmesh-faces are most likely the easiest way for new scripts to create faces,
|
||||
since faces can be added one by one and the api has features intended for mesh manipulation.
|
||||
- BMesh-faces are most likely the easiest way to create faces in new scripts,
|
||||
since faces can be added one by one and the API has features intended for mesh manipulation.
|
||||
While :class:`bmesh.types.BMesh` uses more memory it can be managed by only operating on one mesh at a time.
|
||||
|
||||
|
||||
Editing
|
||||
-------
|
||||
|
||||
Editing is where the 3 data types vary most.
|
||||
Editing is where the three data types vary most.
|
||||
|
||||
- Polygons are very limited for editing,
|
||||
changing materials and options like smooth works but for anything else
|
||||
they are too inflexible and are only intended for storage.
|
||||
- Tessfaces should not be used for editing geometry because doing so will cause existing ngons to be tessellated.
|
||||
- BMesh-Faces are by far the best way to manipulate geometry.
|
||||
- Tessfaces should not be used for editing geometry because doing so will cause existing n-gons to be tessellated.
|
||||
- BMesh-faces are by far the best way to manipulate geometry.
|
||||
|
||||
|
||||
Exporting
|
||||
---------
|
||||
|
||||
All 3 data types can be used for exporting,
|
||||
the choice mostly depends on whether the target format supports ngons or not.
|
||||
All three data types can be used for exporting,
|
||||
the choice mostly depends on whether the target format supports n-gons or not.
|
||||
|
||||
- Polygons are the most direct & efficient way to export providing they convert into the output format easily enough.
|
||||
- Tessfaces work well for exporting to formats which don't support ngons,
|
||||
- Polygons are the most direct and efficient way to export providing they convert into the output format easily enough.
|
||||
- Tessfaces work well for exporting to formats which don't support n-gons,
|
||||
in fact this is the only place where their use is encouraged.
|
||||
- BMesh-Faces can work for exporting too but may not be necessary if polygons can be used
|
||||
since using bmesh gives some overhead because its not the native storage format in object mode.
|
||||
since using BMesh gives some overhead because its not the native storage format in Object-Mode.
|
||||
|
||||
|
||||
EditBones, PoseBones, Bone... Bones
|
||||
===================================
|
||||
Edit Bones, Pose Bones, Bone... Bones
|
||||
=====================================
|
||||
|
||||
Armature Bones in Blender have three distinct data structures that contain them.
|
||||
If you are accessing the bones through one of them, you may not have access to the properties you really need.
|
||||
@ -280,43 +272,41 @@ If you are accessing the bones through one of them, you may not have access to t
|
||||
Edit Bones
|
||||
----------
|
||||
|
||||
``bpy.context.object.data.edit_bones`` contains a editbones;
|
||||
to access them you must set the armature mode to edit mode first (editbones do not exist in object or pose mode).
|
||||
``bpy.context.object.data.edit_bones`` contains an edit bones;
|
||||
to access them you must set the armature mode to Edit-Mode first (edit bones do not exist in Object or Pose-Mode).
|
||||
Use these to create new bones, set their head/tail or roll, change their parenting relationships to other bones, etc.
|
||||
|
||||
Example using :class:`bpy.types.EditBone` in armature editmode:
|
||||
|
||||
This is only possible in edit mode.
|
||||
Example using :class:`bpy.types.EditBone` in armature Edit-Mode
|
||||
which is only possible in Edit-Mode:
|
||||
|
||||
>>> bpy.context.object.data.edit_bones["Bone"].head = Vector((1.0, 2.0, 3.0))
|
||||
|
||||
This will be empty outside of editmode.
|
||||
This will be empty outside of Edit-Mode:
|
||||
|
||||
>>> mybones = bpy.context.selected_editable_bones
|
||||
|
||||
Returns an editbone only in edit mode.
|
||||
Returns an edit bone only in Edit-Mode:
|
||||
|
||||
>>> bpy.context.active_bone
|
||||
|
||||
|
||||
Bones (Object Mode)
|
||||
Bones (Object-Mode)
|
||||
-------------------
|
||||
|
||||
``bpy.context.object.data.bones`` contains bones.
|
||||
These *live* in object mode, and have various properties you can change,
|
||||
These *live* in Object-Mode, and have various properties you can change,
|
||||
note that the head and tail properties are read-only.
|
||||
|
||||
Example using :class:`bpy.types.Bone` in object or pose mode:
|
||||
|
||||
Returns a bone (not an editbone) outside of edit mode
|
||||
Example using :class:`bpy.types.Bone` in Object or Pose-Mode
|
||||
returning a bone (not an edit bone) outside of Edit-Mode:
|
||||
|
||||
>>> bpy.context.active_bone
|
||||
|
||||
This works, as with blender the setting can be edited in any mode
|
||||
This works, as with Blender the setting can be edited in any mode:
|
||||
|
||||
>>> bpy.context.object.data.bones["Bone"].use_deform = True
|
||||
|
||||
Accessible but read-only
|
||||
Accessible but read-only:
|
||||
|
||||
>>> tail = myobj.data.bones["Bone"].tail
|
||||
|
||||
@ -326,42 +316,42 @@ Pose Bones
|
||||
|
||||
``bpy.context.object.pose.bones`` contains pose bones.
|
||||
This is where animation data resides, i.e. animatable transformations
|
||||
are applied to pose bones, as are constraints and ik-settings.
|
||||
are applied to pose bones, as are constraints and IK-settings.
|
||||
|
||||
Examples using :class:`bpy.types.PoseBone` in object or pose mode:
|
||||
Examples using :class:`bpy.types.PoseBone` in Object or Pose-Mode:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# Gets the name of the first constraint (if it exists)
|
||||
bpy.context.object.pose.bones["Bone"].constraints[0].name
|
||||
|
||||
# Gets the last selected pose bone (pose mode only)
|
||||
# Gets the last selected pose bone (Pose-Mode only)
|
||||
bpy.context.active_pose_bone
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
Notice the pose is accessed from the object rather than the object data,
|
||||
this is why blender can have 2 or more objects sharing the same armature in different poses.
|
||||
this is why Blender can have two or more objects sharing the same armature in different poses.
|
||||
|
||||
.. note::
|
||||
|
||||
Strictly speaking PoseBone's are not bones, they are just the state of the armature,
|
||||
Strictly speaking pose bones are not bones, they are just the state of the armature,
|
||||
stored in the :class:`bpy.types.Object` rather than the :class:`bpy.types.Armature`,
|
||||
the real bones are however accessible from the pose bones - :class:`bpy.types.PoseBone.bone`
|
||||
yet the real bones are accessible from the pose bones via :class:`bpy.types.PoseBone.bone`.
|
||||
|
||||
|
||||
Armature Mode Switching
|
||||
-----------------------
|
||||
|
||||
While writing scripts that deal with armatures you may find you have to switch between modes,
|
||||
when doing so take care when switching out of edit-mode not to keep references
|
||||
to the edit-bones or their head/tail vectors.
|
||||
Further access to these will crash blender so its important the script
|
||||
when doing so take care when switching out of Edit-Mode not to keep references
|
||||
to the edit bones or their head/tail vectors.
|
||||
Further access to these will crash Blender so its important the script
|
||||
clearly separates sections of the code which operate in different modes.
|
||||
|
||||
This is mainly an issue with editmode since pose data can be manipulated without having to be in pose mode,
|
||||
however for operator access you may still need to enter pose mode.
|
||||
This is mainly an issue with Edit-Mode since pose data can be manipulated without having to be in Pose-Mode,
|
||||
yet for operator access you may still need to enter Pose-Mode.
|
||||
|
||||
|
||||
Data Names
|
||||
@ -372,8 +362,7 @@ Naming Limitations
|
||||
------------------
|
||||
|
||||
A common mistake is to assume newly created data is given the requested name.
|
||||
|
||||
This can cause bugs when you add some data (normally imported) then reference it later by name.
|
||||
This can cause bugs when you add data (normally imported) then reference it later by name:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -383,7 +372,7 @@ This can cause bugs when you add some data (normally imported) then reference it
|
||||
bpy.data.meshes[meshid]
|
||||
|
||||
|
||||
Or with name assignment...
|
||||
Or with name assignment:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -397,12 +386,12 @@ Data names may not match the assigned values if they exceed the maximum length,
|
||||
|
||||
|
||||
Its better practice not to reference objects by names at all,
|
||||
once created you can store the data in a list, dictionary, on a class etc,
|
||||
once created you can store the data in a list, dictionary, on a class, etc;
|
||||
there is rarely a reason to have to keep searching for the same data by name.
|
||||
|
||||
If you do need to use name references, its best to use a dictionary to maintain
|
||||
a mapping between the names of the imported assets and the newly created data,
|
||||
this way you don't run this risk of referencing existing data from the blend file, or worse modifying it.
|
||||
this way you don't run this risk of referencing existing data from the blend-file, or worse modifying it.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -421,17 +410,14 @@ this way you don't run this risk of referencing existing data from the blend fil
|
||||
Library Collisions
|
||||
------------------
|
||||
|
||||
Blender keeps data names unique - :class:`bpy.types.ID.name` so you can't name two objects,
|
||||
meshes, scenes etc the same thing by accident.
|
||||
|
||||
However when linking in library data from another blend file naming collisions can occur,
|
||||
Blender keeps data names unique (:class:`bpy.types.ID.name`) so you can't name two objects,
|
||||
meshes, scenes, etc., the same by accident.
|
||||
However, when linking in library data from another blend-file naming collisions can occur,
|
||||
so its best to avoid referencing data by name at all.
|
||||
|
||||
This can be tricky at times and not even blender handles this correctly in some case
|
||||
(when selecting the modifier object for eg you can't select between multiple objects with the same name),
|
||||
but its still good to try avoid problems in this area.
|
||||
|
||||
|
||||
This can be tricky at times and not even Blender handles this correctly in some case
|
||||
(when selecting the modifier object for e.g. you can't select between multiple objects with the same name),
|
||||
but its still good to try avoiding these problems in this area.
|
||||
If you need to select between local and library data, there is a feature in ``bpy.data`` members to allow for this.
|
||||
|
||||
.. code-block:: python
|
||||
@ -454,18 +440,17 @@ If you need to select between local and library data, there is a feature in ``bp
|
||||
Relative File Paths
|
||||
===================
|
||||
|
||||
Blenders relative file paths are not compatible with standard Python modules such as ``sys`` and ``os``.
|
||||
Blender's relative file paths are not compatible with standard Python modules such as ``sys`` and ``os``.
|
||||
Built-in Python functions don't understand Blender's ``//`` prefix which denotes the blend-file path.
|
||||
|
||||
Built in Python functions don't understand blenders ``//`` prefix which denotes the blend file path.
|
||||
|
||||
A common case where you would run into this problem is when exporting a material with associated image paths.
|
||||
A common case where you would run into this problem is when exporting a material with associated image paths:
|
||||
|
||||
>>> bpy.path.abspath(image.filepath)
|
||||
|
||||
|
||||
When using blender data from linked libraries there is an unfortunate complication
|
||||
since the path will be relative to the library rather than the open blend file.
|
||||
When the data block may be from an external blend file pass the library argument from the :class:`bpy.types.ID`.
|
||||
When using Blender data from linked libraries there is an unfortunate complication
|
||||
since the path will be relative to the library rather than the open blend-file.
|
||||
When the data block may be from an external blend-file pass the library argument from the :class:`bpy.types.ID`.
|
||||
|
||||
>>> bpy.path.abspath(image.filepath, library=image.library)
|
||||
|
||||
@ -478,19 +463,15 @@ Unicode Problems
|
||||
|
||||
Python supports many different encodings so there is nothing stopping you from
|
||||
writing a script in ``latin1`` or ``iso-8859-15``.
|
||||
See `PEP 263 <https://www.python.org/dev/peps/pep-0263/>`__.
|
||||
|
||||
See `pep-0263 <https://www.python.org/dev/peps/pep-0263/>`_
|
||||
|
||||
However this complicates matters for Blender's Python API because ``.blend`` files don't have an explicit encoding.
|
||||
|
||||
To avoid the problem for Python integration and script authors we have decided all strings in blend files
|
||||
However, this complicates matters for Blender's Python API because ``.blend`` files don't have an explicit encoding.
|
||||
To avoid the problem for Python integration and script authors we have decided all strings in blend-files
|
||||
**must** be ``UTF-8``, ``ASCII`` compatible.
|
||||
|
||||
This means assigning strings with different encodings to an object names for instance will raise an error.
|
||||
|
||||
Paths are an exception to this rule since we cannot ignore the existence of non ``UTF-8`` paths on users file-system.
|
||||
|
||||
This means seemingly harmless expressions can raise errors, eg.
|
||||
Paths are an exception to this rule since the existence of non-UTF-8 paths on user's file system cannot be ignored.
|
||||
This means seemingly harmless expressions can raise errors, e.g:
|
||||
|
||||
>>> print(bpy.data.filepath)
|
||||
UnicodeEncodeError: 'ascii' codec can't encode characters in position 10-21: ordinal not in range(128)
|
||||
@ -501,7 +482,7 @@ This means seemingly harmless expressions can raise errors, eg.
|
||||
TypeError: bpy_struct: item.attr= val: Object.name expected a string type, not str
|
||||
|
||||
|
||||
Here are 2 ways around filesystem encoding issues:
|
||||
Here are two ways around file-system encoding issues:
|
||||
|
||||
>>> print(repr(bpy.data.filepath))
|
||||
|
||||
@ -512,11 +493,11 @@ Here are 2 ways around filesystem encoding issues:
|
||||
|
||||
|
||||
Unicode encoding/decoding is a big topic with comprehensive Python documentation,
|
||||
to avoid getting stuck too deep in encoding problems - here are some suggestions:
|
||||
to keep it short about encoding problems -- here are some suggestions:
|
||||
|
||||
- Always use utf-8 encoding or convert to utf-8 where the input is unknown.
|
||||
- Avoid manipulating filepaths as strings directly, use ``os.path`` functions instead.
|
||||
- Use ``os.fsencode()`` / ``os.fsdecode()`` instead of built in string decoding functions when operating on paths.
|
||||
- Always use UTF-8 encoding or convert to UTF-8 where the input is unknown.
|
||||
- Avoid manipulating file paths as strings directly, use ``os.path`` functions instead.
|
||||
- Use ``os.fsencode()`` or ``os.fsdecode()`` instead of built-in string decoding functions when operating on paths.
|
||||
- To print paths or to include them in the user interface use ``repr(path)`` first
|
||||
or ``"%r" % path`` with string formatting.
|
||||
|
||||
@ -528,11 +509,11 @@ to avoid getting stuck too deep in encoding problems - here are some suggestions
|
||||
some importers do this.
|
||||
|
||||
|
||||
Strange errors using 'threading' module
|
||||
=======================================
|
||||
Strange Errors when Using the 'Threading' Module
|
||||
================================================
|
||||
|
||||
Python threading with Blender only works properly when the threads finish up before the script does.
|
||||
By using ``threading.join()`` for example.
|
||||
Python threading with Blender only works properly when the threads finish up before the script does,
|
||||
for example by using ``threading.join()``.
|
||||
|
||||
Here is an example of threading supported by Blender:
|
||||
|
||||
@ -571,8 +552,8 @@ Here is an example of threading supported by Blender:
|
||||
t.join()
|
||||
|
||||
|
||||
This an example of a timer which runs many times a second and moves
|
||||
the default cube continuously while Blender runs **(Unsupported)**.
|
||||
This an example of a timer which runs many times a second
|
||||
and moves the default cube continuously while Blender runs **(Unsupported)**.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -592,33 +573,33 @@ the default cube continuously while Blender runs **(Unsupported)**.
|
||||
Use cases like the one above which leave the thread running once the script finishes
|
||||
may seem to work for a while but end up causing random crashes or errors in Blender's own drawing code.
|
||||
|
||||
So far, no work has gone into making Blender's Python integration thread safe,
|
||||
so until its properly supported, best not make use of this.
|
||||
So far, no work has been done to make Blender's Python integration thread safe,
|
||||
so until it's properly supported, it's best not make use of this.
|
||||
|
||||
.. note::
|
||||
|
||||
Pythons threads only allow co-currency and won't speed up your scripts on multi-processor systems,
|
||||
the ``subprocess`` and ``multiprocess`` modules can be used with Blender and make use of multiple CPU's too.
|
||||
Python threads only allow concurrency and won't speed up your scripts on multiprocessor systems,
|
||||
the ``subprocess`` and ``multiprocess`` modules can be used with Blender to make use of multiple CPUs too.
|
||||
|
||||
|
||||
Help! My script crashes Blender
|
||||
===============================
|
||||
|
||||
**TL;DR:** Do not keep direct references to Blender data (of any kind) when modifying the container
|
||||
of that data, and/or when some undo/redo may happen (e.g. during modal operators execution...).
|
||||
:abbr:`TL;DR (Too long; didn't read.)` Do not keep direct references to Blender data (of any kind)
|
||||
when modifying the container of that data, and/or when some undo/redo may happen
|
||||
(e.g. during modal operators execution...).
|
||||
Instead, use indices (or other data always stored by value in Python, like string keys...),
|
||||
that allow you to get access to the desired data.
|
||||
|
||||
Ideally it would be impossible to crash Blender from Python
|
||||
however there are some problems with the API where it can be made to crash.
|
||||
|
||||
Ideally it would be impossible to crash Blender from Python,
|
||||
however, there are some problems with the API where it can be made to crash.
|
||||
Strictly speaking this is a bug in the API but fixing it would mean adding memory verification
|
||||
on every access since most crashes are caused by the Python objects referencing Blenders memory directly,
|
||||
on every access since most crashes are caused by the Python objects referencing Blender's memory directly,
|
||||
whenever the memory is freed or re-allocated, further Python access to it can crash the script.
|
||||
But fixing this would make the scripts run very slow,
|
||||
or writing a very different kind of API which doesn't reference the memory directly.
|
||||
|
||||
Here are some general hints to avoid running into these problems.
|
||||
Here are some general hints to avoid running into these problems:
|
||||
|
||||
- Be aware of memory limits,
|
||||
especially when working with large lists since Blender can crash simply by running out of memory.
|
||||
@ -631,16 +612,16 @@ Here are some general hints to avoid running into these problems.
|
||||
- Modules or classes that remain active while Blender is used,
|
||||
should not hold references to data the user may remove, instead,
|
||||
fetch data from the context each time the script is activated.
|
||||
- Crashes may not happen every time, they may happen more on some configurations/operating-systems.
|
||||
- Be wary of recursive patterns, those are very efficient at hiding the issues described here.
|
||||
- See last sub-section about `Unfortunate Corner Cases`_ for some known breaking exceptions.
|
||||
- Crashes may not happen every time, they may happen more on some configurations or operating systems.
|
||||
- Be careful with recursive patterns, those are very efficient at hiding the issues described here.
|
||||
- See last subsection about `Unfortunate Corner Cases`_ for some known breaking exceptions.
|
||||
|
||||
.. note::
|
||||
|
||||
To find the line of your script that crashes you can use the ``faulthandler`` module.
|
||||
See the `faulthandler docs <https://docs.python.org/dev/library/faulthandler.html>`_.
|
||||
See the `Faulthandler docs <https://docs.python.org/dev/library/faulthandler.html>`__.
|
||||
|
||||
While the crash may be in Blenders C/C++ code,
|
||||
While the crash may be in Blender's C/C++ code,
|
||||
this can help a lot to track down the area of the script that causes the crash.
|
||||
|
||||
.. note::
|
||||
@ -654,7 +635,7 @@ Here are some general hints to avoid running into these problems.
|
||||
in any possible way.
|
||||
|
||||
|
||||
**Don’t:**
|
||||
.. rubric:: Do not:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -673,7 +654,7 @@ Here are some general hints to avoid running into these problems.
|
||||
first_item.name = "foobar"
|
||||
|
||||
|
||||
**Do:**
|
||||
.. rubric:: Do:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -696,33 +677,31 @@ Here are some general hints to avoid running into these problems.
|
||||
Undo/Redo
|
||||
---------
|
||||
|
||||
Undo invalidates all :class:`bpy.types.ID` instances (Object, Scene, Mesh, Lamp... etc).
|
||||
Undo invalidates all :class:`bpy.types.ID` instances (Object, Scene, Mesh, Light, etc.).
|
||||
|
||||
This example shows how you can tell undo changes the memory locations.
|
||||
This example shows how you can tell undo changes the memory locations:
|
||||
|
||||
>>> hash(bpy.context.object)
|
||||
-9223372036849950810
|
||||
>>> hash(bpy.context.object)
|
||||
-9223372036849950810
|
||||
|
||||
# ... move the active object, then undo
|
||||
Move the active object, then undo:
|
||||
|
||||
>>> hash(bpy.context.object)
|
||||
-9223372036849951740
|
||||
|
||||
As suggested above, simply not holding references to data when Blender is used
|
||||
interactively by the user is the only way to ensure the script doesn't become unstable.
|
||||
interactively by the user is the only way to make sure that the script doesn't become unstable.
|
||||
|
||||
|
||||
Undo & Library Data
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
One of the advantages with Blenders library linking system that undo
|
||||
One of the advantages with Blender's library linking system that undo
|
||||
can skip checking changes in library data since it is assumed to be static.
|
||||
|
||||
Tools in Blender are not allowed to modify library data.
|
||||
|
||||
Python however does not enforce this restriction.
|
||||
But Python does not enforce this restriction.
|
||||
|
||||
This can be useful in some cases, using a script to adjust material values for example.
|
||||
But its also possible to use a script to make library data point to newly created local data,
|
||||
@ -733,13 +712,13 @@ So it's best to consider modifying library data an advanced usage of the API
|
||||
and only to use it when you know what you're doing.
|
||||
|
||||
|
||||
Edit Mode / Memory Access
|
||||
Edit-Mode / Memory Access
|
||||
-------------------------
|
||||
|
||||
Switching edit-mode ``bpy.ops.object.mode_set(mode='EDIT')`` / ``bpy.ops.object.mode_set(mode='OBJECT')``
|
||||
Switching mode ``bpy.ops.object.mode_set(mode='EDIT')`` or ``bpy.ops.object.mode_set(mode='OBJECT')``
|
||||
will re-allocate objects data,
|
||||
any references to a meshes vertices/polygons/uvs, armatures bones,
|
||||
curves points etc cannot be accessed after switching edit-mode.
|
||||
any references to a meshes vertices/polygons/UVs, armatures bones,
|
||||
curves points, etc. cannot be accessed after switching mode.
|
||||
|
||||
Only the reference to the data its self can be re-accessed, the following example will crash.
|
||||
|
||||
@ -754,7 +733,7 @@ Only the reference to the data its self can be re-accessed, the following exampl
|
||||
print(polygons)
|
||||
|
||||
|
||||
So after switching edit-mode you need to re-access any object data variables,
|
||||
So after switching mode you need to re-access any object data variables,
|
||||
the following example shows how to avoid the crash above.
|
||||
|
||||
.. code-block:: python
|
||||
@ -770,7 +749,7 @@ the following example shows how to avoid the crash above.
|
||||
|
||||
|
||||
These kinds of problems can happen for any functions which re-allocate
|
||||
the object data but are most common when switching edit-mode.
|
||||
the object data but are most common when switching mode.
|
||||
|
||||
|
||||
Array Re-Allocation
|
||||
@ -791,21 +770,20 @@ internally the array which stores this data is re-allocated.
|
||||
This can be avoided by re-assigning the point variables after adding the new one or by storing
|
||||
indices to the points rather than the points themselves.
|
||||
|
||||
The best way is to sidestep the problem altogether add all the points to the curve at once.
|
||||
This means you don't have to worry about array re-allocation and its faster too
|
||||
since reallocating the entire array for every point added is inefficient.
|
||||
The best way is to sidestep the problem altogether by adding all the points to the curve at once.
|
||||
This means you don't have to worry about array re-allocation and it's faster too
|
||||
since re-allocating the entire array for every added point is inefficient.
|
||||
|
||||
|
||||
Removing Data
|
||||
-------------
|
||||
|
||||
**Any** data that you remove shouldn't be modified or accessed afterwards,
|
||||
this includes f-curves, drivers, render layers, timeline markers, modifiers, constraints
|
||||
along with objects, scenes, collections, bones.. etc.
|
||||
this includes: F-curves, drivers, render layers, timeline markers, modifiers, constraints
|
||||
along with objects, scenes, collections, bones, etc.
|
||||
|
||||
The ``remove()`` api calls will invalidate the data they free to prevent common mistakes.
|
||||
|
||||
The following example shows how this precaution works.
|
||||
The ``remove()`` API calls will invalidate the data they free to prevent common mistakes.
|
||||
The following example shows how this precaution works:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -818,7 +796,7 @@ The following example shows how this precaution works.
|
||||
|
||||
|
||||
But take care because this is limited to scripts accessing the variable which is removed,
|
||||
the next example will still crash.
|
||||
the next example will still crash:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -835,8 +813,8 @@ Besides all expected cases listed above, there are a few others that should not
|
||||
an issue but, due to internal implementation details, currently are:
|
||||
|
||||
- ``Object.hide_viewport``, ``Object.hide_select`` and ``Object.hide_render``:
|
||||
Setting any of those booleans will trigger a rebuild of Collection caches, hence breaking
|
||||
any current iteration over ``Collection.all_objects``.
|
||||
Setting any of those Booleans will trigger a rebuild of Collection caches,
|
||||
thus breaking any current iteration over ``Collection.all_objects``.
|
||||
|
||||
|
||||
sys.exit
|
||||
@ -848,5 +826,5 @@ as if Blender is crashing since ``sys.exit()`` will close Blender immediately.
|
||||
|
||||
For example, the ``argparse`` module will print an error and exit if the arguments are invalid.
|
||||
|
||||
An ugly way of troubleshooting this is to set ``sys.exit = None`` and see what line of Python code is quitting,
|
||||
An dirty way of troubleshooting this is to set ``sys.exit = None`` and see what line of Python code is quitting,
|
||||
you could of course replace ``sys.exit`` with your own function but manipulating Python in this way is bad practice.
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
.. _info_overview:
|
||||
|
||||
*******************
|
||||
@ -6,24 +5,24 @@ Python API Overview
|
||||
*******************
|
||||
|
||||
The purpose of this document is to explain how Python and Blender fit together,
|
||||
covering some of the functionality that may not be obvious from reading the API
|
||||
references and example scripts.
|
||||
covering some of the functionality that may not be obvious from reading the API references
|
||||
and example scripts.
|
||||
|
||||
|
||||
Python in Blender
|
||||
=================
|
||||
|
||||
Blender has an embedded Python interpreter which is loaded when Blender is started and stays
|
||||
active while Blender is running. This interpreter runs scripts to draw the user interface
|
||||
and is used for some of Blender’s internal tools as well.
|
||||
Blender has an embedded Python interpreter which is loaded when Blender is started
|
||||
and stays active while Blender is running. This interpreter runs scripts to draw the user interface
|
||||
and is used for some of Blender's internal tools as well.
|
||||
|
||||
Blender's embedded interpreter provides a typical Python environment, so code from tutorials
|
||||
on how to write Python scripts can also be run with Blender’s interpreter. Blender provides its
|
||||
on how to write Python scripts can also be run with Blender's interpreter. Blender provides its
|
||||
Python modules, such as :mod:`bpy` and :mod:`mathutils`, to the embedded interpreter so they can
|
||||
be imported into a script and give access to Blender's data, classes, and functions. Scripts that
|
||||
deal with Blender data will need to import the modules to work.
|
||||
be imported into a script and give access to Blender's data, classes, and functions.
|
||||
Scripts that deal with Blender data will need to import the modules to work.
|
||||
|
||||
Here is a simple example which moves a vertex attached to an object named **Cube**:
|
||||
Here is a simple example which moves a vertex attached to an object named "Cube":
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -31,7 +30,7 @@ Here is a simple example which moves a vertex attached to an object named **Cube
|
||||
bpy.data.objects["Cube"].data.vertices[0].co.x += 1.0
|
||||
|
||||
This modifies Blender's internal data directly.
|
||||
When you run this in the interactive console you will see the 3D viewport update.
|
||||
When you run this in the interactive console you will see the 3D Viewport update.
|
||||
|
||||
|
||||
The Default Environment
|
||||
@ -41,7 +40,7 @@ When developing your own scripts it may help to understand how Blender sets up i
|
||||
Many Python scripts come bundled with Blender and can be used as a reference
|
||||
because they use the same API that script authors write tools in.
|
||||
Typical usage for scripts include: user interface, import/export,
|
||||
scene manipulation, automation, defining your own toolset and customization.
|
||||
scene manipulation, automation, defining your own tool set and customization.
|
||||
|
||||
On startup Blender scans the ``scripts/startup/`` directory for Python modules and imports them.
|
||||
The exact location of this directory depends on your installation.
|
||||
@ -54,8 +53,8 @@ Script Loading
|
||||
This may seem obvious, but it is important to note the difference between
|
||||
executing a script directly and importing a script as a module.
|
||||
|
||||
Extending Blender by executing a script directly means the classes that the script
|
||||
defines remain available inside Blender after the script finishes execution.
|
||||
Extending Blender by executing a script directly means the classes that the script defines
|
||||
remain available inside Blender after the script finishes execution.
|
||||
Using scripts this way makes future access to their classes
|
||||
(to unregister them for example) more difficult compared to importing the scripts as modules.
|
||||
When a script is imported as a module, its class instances will remain
|
||||
@ -63,12 +62,11 @@ inside the module and can be accessed later on by importing that module again.
|
||||
|
||||
For this reason it is preferable to avoid directly executing scripts that extend Blender by registering classes.
|
||||
|
||||
Here are some ways to run scripts directly in Blender:
|
||||
|
||||
Here are some ways to run scripts directly in Blender.
|
||||
|
||||
- Loaded in the text editor and press **Run Script**.
|
||||
- Loaded in the text editor and press *Run Script*.
|
||||
- Typed or pasted into the interactive console.
|
||||
- Execute a Python file from the command line with Blender, eg:
|
||||
- Execute a Python file from the command line with Blender, e.g:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
@ -77,24 +75,22 @@ Here are some ways to run scripts directly in Blender.
|
||||
|
||||
To run as modules:
|
||||
|
||||
- The obvious way, ``import some_module`` command from the text window or interactive console.
|
||||
- Open as a text block and tick "Register" option, this will load with the blend file.
|
||||
- copy into one of the directories ``scripts/startup``, where they will be automatically imported on startup.
|
||||
- define as an add-on, enabling the add-on will load it as a Python module.
|
||||
- The obvious way, ``import some_module`` command from the text editor or interactive console.
|
||||
- Open as a text data-block and check the *Register* option, this will load with the blend-file.
|
||||
- Copy into one of the directories ``scripts/startup``, where they will be automatically imported on startup.
|
||||
- Define as an add-on, enabling the add-on will load it as a Python module.
|
||||
|
||||
|
||||
Add-ons
|
||||
-------
|
||||
|
||||
Some of Blenders functionality is best kept optional,
|
||||
alongside scripts loaded at startup we have add-ons which are kept in their own directory ``scripts/addons``,
|
||||
and only load on startup if selected from the user preferences.
|
||||
|
||||
The only difference between add-ons and built-in Python modules is that add-ons must contain a ``bl_info``
|
||||
variable which Blender uses to read metadata such as name, author, category and URL.
|
||||
|
||||
The User Preferences add-on listing uses **bl_info** to display information about each add-on.
|
||||
Some of Blender's functionality is best kept optional,
|
||||
alongside scripts loaded at startup there are add-ons which are kept in their own directory ``scripts/addons``,
|
||||
They are only loaded on startup if selected from the user preferences.
|
||||
|
||||
The only difference between add-ons and built-in Python modules is that add-ons must contain a ``bl_info`` variable
|
||||
which Blender uses to read metadata such as name, author, category and project link.
|
||||
The User Preferences add-on listing uses ``bl_info`` to display information about each add-on.
|
||||
`See Add-ons <https://wiki.blender.org/index.php/Dev:Py/Scripts/Guidelines/Addons>`__
|
||||
for details on the ``bl_info`` dictionary.
|
||||
|
||||
@ -105,7 +101,7 @@ Integration through Classes
|
||||
Running Python scripts in the text editor is useful for testing but you'll
|
||||
want to extend Blender to make tools accessible like other built-in functionality.
|
||||
|
||||
The Blender Python api allows integration for:
|
||||
The Blender Python API allows integration for:
|
||||
|
||||
- :class:`bpy.types.Panel`
|
||||
- :class:`bpy.types.Menu`
|
||||
@ -114,13 +110,12 @@ The Blender Python api allows integration for:
|
||||
- :class:`bpy.types.KeyingSet`
|
||||
- :class:`bpy.types.RenderEngine`
|
||||
|
||||
|
||||
This is intentionally limited. Currently, for more advanced features such as mesh modifiers,
|
||||
object types, or shader nodes, C/C++ must be used.
|
||||
|
||||
For Python integration Blender defines methods which are common to all types.
|
||||
This works by creating a Python subclass of a Blender class which contains variables and functions
|
||||
specified by the parent class which are pre-defined to interface with Blender.
|
||||
specified by the parent class which are predefined to interface with Blender.
|
||||
|
||||
For example:
|
||||
|
||||
@ -137,22 +132,20 @@ For example:
|
||||
|
||||
bpy.utils.register_class(SimpleOperator)
|
||||
|
||||
First note that we subclass a member of :mod:`bpy.types`,
|
||||
First note that it defines a subclass as a member of :mod:`bpy.types`,
|
||||
this is common for all classes which can be integrated with Blender and
|
||||
used so we know if this is an Operator and not a Panel when registering.
|
||||
is used to distinguish an Operator from a Panel when registering.
|
||||
|
||||
Both class properties start with a ``bl_`` prefix.
|
||||
This is a convention used to distinguish Blender properties from those you add yourself.
|
||||
|
||||
Next see the execute function, which takes an instance of the operator and the current context.
|
||||
A common prefix is not used for functions.
|
||||
|
||||
Lastly the register function is called, this takes the class and loads it into Blender. See `Class Registration`_.
|
||||
|
||||
Regarding inheritance, Blender doesn't impose restrictions on the kinds of class inheritance used,
|
||||
the registration checks will use attributes and functions defined in parent classes.
|
||||
|
||||
class mix-in example:
|
||||
Class mix-in example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -173,8 +166,8 @@ While ``__init__()`` and ``__del__()`` will be called if defined,
|
||||
the class instances lifetime only spans the execution.
|
||||
So a panel for example will have a new instance for every redraw,
|
||||
for this reason there is rarely a cause to store variables in the panel instance.
|
||||
Instead, persistent variables should be stored in Blenders
|
||||
ata so that the state can be restored when Blender is restarted.
|
||||
Instead, persistent variables should be stored in Blender's data
|
||||
so that the state can be restored when Blender is restarted.
|
||||
|
||||
.. note::
|
||||
|
||||
@ -182,15 +175,14 @@ ata so that the state can be restored when Blender is restarted.
|
||||
|
||||
So once the class is registered with Blender, instancing the class and calling the functions is left up to Blender.
|
||||
In fact you cannot instance these classes from the script as you would expect with most Python API's.
|
||||
|
||||
To run operators you can call them through the operator api, eg:
|
||||
To run operators you can call them through the operator API, e.g:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import bpy
|
||||
bpy.ops.object.simple_operator()
|
||||
|
||||
User interface classes are given a context in which to draw, buttons window, file header, toolbar etc,
|
||||
User interface classes are given a context in which to draw, buttons, window, file header, toolbar, etc.,
|
||||
then they are drawn when that area is displayed so they are never called by Python scripts directly.
|
||||
|
||||
|
||||
@ -205,7 +197,7 @@ Module Registration
|
||||
Blender modules loaded at startup require ``register()`` and ``unregister()`` functions.
|
||||
These are the *only* functions that Blender calls from your code, which is otherwise a regular Python module.
|
||||
|
||||
A simple Blender/Python module can look like this:
|
||||
A simple Blender Python module can look like this:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -225,16 +217,15 @@ A simple Blender/Python module can look like this:
|
||||
|
||||
These functions usually appear at the bottom of the script containing class registration sometimes adding menu items.
|
||||
You can also use them for internal purposes setting up data for your own tools but take care
|
||||
since register won't re-run when a new blend file is loaded.
|
||||
since register won't re-run when a new blend-file is loaded.
|
||||
|
||||
The register/unregister calls are used so it's possible to toggle add-ons and reload scripts while Blender runs.
|
||||
If the register calls were placed in the body of the script, registration would be called on import,
|
||||
meaning there would be no distinction between importing a module or loading its classes into Blender.
|
||||
|
||||
This becomes problematic when a script imports classes from another module
|
||||
making it difficult to manage which classes are being loaded and when.
|
||||
|
||||
The last 2 lines are only for testing:
|
||||
The last two lines are only for testing:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -251,7 +242,6 @@ Class Registration
|
||||
|
||||
Registering a class with Blender results in the class definition being loaded into Blender,
|
||||
where it becomes available alongside existing functionality.
|
||||
|
||||
Once this class is loaded you can access it from :mod:`bpy.types`,
|
||||
using the ``bl_idname`` rather than the classes original name.
|
||||
|
||||
@ -271,23 +261,23 @@ Using the function arguments ``def execute(self, context, spam)``, will raise an
|
||||
|
||||
``ValueError: expected Operator, SimpleOperator class "execute" function to have 2 args, found 3``
|
||||
|
||||
Using ``bl_idname = 1`` will raise.
|
||||
Using ``bl_idname = 1`` will raise:
|
||||
|
||||
``TypeError: validating class error: Operator.bl_idname expected a string type, not int``
|
||||
|
||||
|
||||
Inter Classes Dependencies
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Inter-Class Dependencies
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
When customizing Blender you may want to group your own settings together,
|
||||
after all, they will likely have to co-exist with other scripts.
|
||||
To group these properties classes need to be defined,
|
||||
for groups within groups or collections within groups
|
||||
you can find yourself having to deal with order of registration/unregistration.
|
||||
you can't avoid having to deal with the order of registration/unregistration.
|
||||
|
||||
Custom properties groups are themselves classes which need to be registered.
|
||||
|
||||
Say you want to store material settings for a custom engine.
|
||||
For example, if you want to store material settings for a custom engine:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -311,7 +301,7 @@ Say you want to store material settings for a custom engine.
|
||||
|
||||
.. note::
|
||||
|
||||
*The class must be registered before being used in a property, failing to do so will raise an error:*
|
||||
The class **must be** registered before being used in a property, failing to do so will raise an error:
|
||||
|
||||
``ValueError: bpy_struct "Material" registration error: my_custom_props could not register``
|
||||
|
||||
@ -341,17 +331,17 @@ Say you want to store material settings for a custom engine.
|
||||
if __name__ == "__main__":
|
||||
register()
|
||||
|
||||
.. note::
|
||||
.. important::
|
||||
|
||||
*The lower most class needs to be registered first and that unregister() is a mirror of register()*
|
||||
The lower most class needs to be registered first and that ``unregister()`` is a mirror of ``register()``.
|
||||
|
||||
|
||||
Manipulating Classes
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Properties can be added and removed as Blender runs,
|
||||
normally happens on register or unregister but for some
|
||||
special cases it may be useful to modify types as the script runs.
|
||||
normally done on register or unregister but for some special cases
|
||||
it may be useful to modify types as the script runs.
|
||||
|
||||
For example:
|
||||
|
||||
@ -362,7 +352,7 @@ For example:
|
||||
# remove
|
||||
del bpy.types.Object.my_float
|
||||
|
||||
This works just as well for PropertyGroup subclasses you define yourself.
|
||||
This works just as well for ``PropertyGroup`` subclasses you define yourself.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -370,7 +360,7 @@ This works just as well for PropertyGroup subclasses you define yourself.
|
||||
pass
|
||||
MyPropGroup.my_float: bpy.props.FloatProperty()
|
||||
|
||||
...this is equivalent to:
|
||||
This is equivalent to:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -378,11 +368,11 @@ This works just as well for PropertyGroup subclasses you define yourself.
|
||||
my_float: bpy.props.FloatProperty()
|
||||
|
||||
|
||||
Dynamic Defined-Classes (Advanced)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Dynamic Class Definition (Advanced)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
In some cases the specifier for data may not be in Blender, renderman shader definitions
|
||||
for example, and it may be useful to define them as types and remove them on the fly.
|
||||
In some cases the specifier for data may not be in Blender, for example a external render engines shader definitions,
|
||||
and it may be useful to define them as types and remove them on the fly.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
|
@ -1,19 +1,16 @@
|
||||
|
||||
.. _info_quickstart:
|
||||
|
||||
***********************
|
||||
Quickstart Introduction
|
||||
***********************
|
||||
**********
|
||||
Quickstart
|
||||
**********
|
||||
|
||||
Preface
|
||||
=======
|
||||
This :abbr:`API (Application Programming Interface)` is generally stable
|
||||
but some areas are still being extended and improved.
|
||||
|
||||
This API is generally stable but some areas are still being added and improved.
|
||||
|
||||
The Blender/Python API can do the following:
|
||||
.. rubric:: Blender Python API features:
|
||||
|
||||
- Edit any data the user interface can (Scenes, Meshes, Particles etc.).
|
||||
- Modify user preferences, key-maps and themes.
|
||||
- Modify user preferences, keymaps and themes.
|
||||
- Run tools with own settings.
|
||||
- Create user interface elements such as menus, headers and panels.
|
||||
- Create new tools.
|
||||
@ -21,10 +18,10 @@ The Blender/Python API can do the following:
|
||||
- Create new rendering engines that integrate with Blender.
|
||||
- Subscribe to changes to data and it's properties.
|
||||
- Define new settings in existing Blender data.
|
||||
- Draw in the 3D view using Python.
|
||||
- Draw in the 3D Viewport using Python.
|
||||
|
||||
|
||||
The Blender/Python API **can't** (yet)...
|
||||
.. rubric:: (Still) missing features:
|
||||
|
||||
- Create new space types.
|
||||
- Assign custom properties to every type.
|
||||
@ -33,22 +30,21 @@ The Blender/Python API **can't** (yet)...
|
||||
Before Starting
|
||||
===============
|
||||
|
||||
This document isn't intended to fully cover each topic.
|
||||
Rather, its purpose is to familiarize you with Blender Python API.
|
||||
|
||||
This document its intended to familiarize you with Blender Python API
|
||||
but not to fully cover each topic.
|
||||
|
||||
A quick list of helpful things to know before starting:
|
||||
|
||||
- Blender uses Python 3.x; some online documentation still assumes 2.x.
|
||||
- Blender uses Python 3.x; some online documentation still assumes version 2.x.
|
||||
- The interactive console is great for testing one-liners.
|
||||
It also has autocompletion so you can inspect the API quickly.
|
||||
- Button tool tips 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.
|
||||
- Button tooltips show Python attributes and operator names.
|
||||
- The context menu of buttons directly links to this API documentation.
|
||||
- More operator examples can be found in the text editor's template menu.
|
||||
- To examine further scripts distributed with Blender, see:
|
||||
|
||||
| ``scripts/startup/bl_ui`` for the user interface,
|
||||
| ``scripts/startup/bl_operators`` for operators.
|
||||
- ``scripts/startup/bl_ui`` for the user interface.
|
||||
- ``scripts/startup/bl_operators`` for operators.
|
||||
|
||||
Exact location depends on platform, see:
|
||||
:ref:`directory layout docs <blender_manual:blender-directory-layout>`.
|
||||
@ -59,19 +55,14 @@ Running Scripts
|
||||
|
||||
The two most common ways to execute Python scripts are using the built-in
|
||||
text editor or entering commands in the Python console.
|
||||
|
||||
Both the *Text Editor* and *Python Console* are space types you can select from the view header.
|
||||
|
||||
Both the *Text Editor* and *Python Console* are space types you can select from the header.
|
||||
Rather than manually configuring your spaces for Python development,
|
||||
you may prefer to use the *Scripting* screen, included default with Blender,
|
||||
accessible from the top headers screen selector.
|
||||
you can use the *Scripting* workspace accessible from the Topbar tabs.
|
||||
|
||||
From the text editor you can open ``.py`` files or paste then from the clipboard, then test using *Run Script*.
|
||||
|
||||
The Python Console is typically used for typing in snippets and for testing to get immediate feedback,
|
||||
but can also have entire scripts pasted into it.
|
||||
|
||||
Scripts can also run from the command line with Blender but to learn Blender/Python this isn't essential.
|
||||
Scripts can also run from the command line with Blender but to learn scripting in Blender this isn't essential.
|
||||
|
||||
|
||||
Key Concepts
|
||||
@ -80,14 +71,13 @@ Key Concepts
|
||||
Data Access
|
||||
-----------
|
||||
|
||||
Accessing DataBlocks
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
Accessing Data-Blocks
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Python accesses Blender's data in the same way as the animation system and user interface;
|
||||
this implies that any setting that can be changed via a button can also be changed from Python.
|
||||
|
||||
Accessing data from the currently loaded blend file is done with the module :mod:`bpy.data`.
|
||||
This gives access to library data. For example:
|
||||
You can access Blender's data with the Python API in the same way as the animation system or user interface;
|
||||
this implies that any setting that can be changed via a button can also be changed with Python.
|
||||
Accessing data from the currently loaded blend-file is done with the module :mod:`bpy.data`.
|
||||
It gives access to library data, for example:
|
||||
|
||||
>>> bpy.data.objects
|
||||
<bpy_collection[3], BlendDataObjects>
|
||||
@ -99,12 +89,11 @@ This gives access to library data. For example:
|
||||
<bpy_collection[1], BlendDataMaterials>
|
||||
|
||||
|
||||
About Collections
|
||||
^^^^^^^^^^^^^^^^^
|
||||
Accessing 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;
|
||||
You will notice that an index as well as a string can be used to access members of the collection.
|
||||
Unlike Python dictionaries, both methods are available;
|
||||
however, the index of a member may change while running Blender.
|
||||
|
||||
>>> list(bpy.data.objects)
|
||||
@ -120,7 +109,7 @@ however, the index of a member may change while running Blender.
|
||||
Accessing Attributes
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Once you have a data block, such as a material, object, collections etc.,
|
||||
Once you have a data-block, such as a material, object, collection, etc.,
|
||||
its attributes can be accessed much like you would change a setting using the graphical interface.
|
||||
In fact, the tooltip for each button also displays the Python attribute
|
||||
which can help in finding what settings to change in a script.
|
||||
@ -135,8 +124,8 @@ which can help in finding what settings to change in a script.
|
||||
bpy.data.materials['MyMaterial']
|
||||
|
||||
|
||||
For testing what data to access it's useful to use the "Console", which is its own space type.
|
||||
This supports auto-complete, giving you a fast way to dig into different data in your file.
|
||||
For testing what data to access it's useful to use the Python Console, which is its own space type.
|
||||
This supports auto-complete, giving you a fast way to explore the data in your file.
|
||||
|
||||
Example of a data path that can be quickly found via the console:
|
||||
|
||||
@ -149,8 +138,8 @@ Example of a data path that can be quickly found via the console:
|
||||
Data Creation/Removal
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Those of you familiar with other Python API's may be surprised that
|
||||
new data-blocks in the bpy API can't be created by calling the class:
|
||||
When you are familiar with other Python APIs you may be surprised that
|
||||
new data-blocks in the bpy API cannot be created by calling the class:
|
||||
|
||||
>>> bpy.types.Mesh()
|
||||
Traceback (most recent call last):
|
||||
@ -159,10 +148,10 @@ new data-blocks in the bpy API can't be created by calling the class:
|
||||
|
||||
|
||||
This is an intentional part of the API design.
|
||||
The Blender/Python API can't create Blender data that exists outside the main Blender database
|
||||
(accessed through :mod:`bpy.data`), because this data is managed by Blender (save/load/undo/append... etc).
|
||||
The Blender Python API can't create Blender data that exists outside the main Blender database
|
||||
(accessed through :mod:`bpy.data`), because this data is managed by Blender (save, load, undo, append, etc).
|
||||
|
||||
Data is added and removed via methods on the collections in :mod:`bpy.data`, eg:
|
||||
Data is added and removed via methods on the collections in :mod:`bpy.data`, e.g:
|
||||
|
||||
>>> mesh = bpy.data.meshes.new(name="MyMesh")
|
||||
>>> print(mesh)
|
||||
@ -174,14 +163,12 @@ Data is added and removed via methods on the collections in :mod:`bpy.data`, eg:
|
||||
Custom Properties
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
Python can access properties on any datablock that has an ID
|
||||
(data that can be linked in and accessed from :mod:`bpy.data`.
|
||||
When assigning a property, you can make up your own names,
|
||||
these will be created when needed or overwritten if they exist.
|
||||
Python can access properties on any data-block that has an ID
|
||||
(data that can be linked in and accessed from :mod:`bpy.data`).
|
||||
When assigning a property, you can pick your own names,
|
||||
these will be created when needed or overwritten if they already exist.
|
||||
|
||||
This data is saved with the blend file and copied with objects.
|
||||
|
||||
Example:
|
||||
This data is saved with the blend-file and copied with objects, for example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -201,10 +188,10 @@ Example:
|
||||
del collection["MySettings"]
|
||||
|
||||
|
||||
Note that these properties can only be assigned basic Python types.
|
||||
Note that these properties can only be assigned basic Python types:
|
||||
|
||||
- int, float, string
|
||||
- array of ints/floats
|
||||
- array of ints or floats
|
||||
- dictionary (only string keys are supported, values must be basic types too)
|
||||
|
||||
These properties are valid outside of Python. They can be animated by curves or used in driver paths.
|
||||
@ -218,18 +205,16 @@ 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:
|
||||
Some common use cases are:
|
||||
|
||||
>>> 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.
|
||||
Note that the context is read-only, which means that these values cannot be modified directly.
|
||||
But they can be changed by running API functions or by using the data API.
|
||||
|
||||
So ``bpy.context.active_object = obj`` will raise an error.
|
||||
|
||||
But ``bpy.context.view_layer.objects.active = obj`` works as expected.
|
||||
|
||||
The context attributes change depending on where they are accessed.
|
||||
@ -257,7 +242,7 @@ Examples:
|
||||
|
||||
.. tip::
|
||||
|
||||
The :ref:`Operator Cheat Sheet <blender_manual:bpy.ops.wm.operator_cheat_sheet>`.
|
||||
The :ref:`Operator Cheat Sheet <blender_manual:bpy.ops.wm.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 all Blender's operators.
|
||||
|
||||
@ -265,8 +250,8 @@ Examples:
|
||||
Operator Poll()
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
Many operators have a "poll" function which may check that the cursor
|
||||
is in a valid area or that the object is in the correct mode (Edit Mode, Weight Paint etc).
|
||||
Many operators have a "poll" function which checks if the cursor
|
||||
is in a valid area or if the object is in the correct mode (Edit Mode, Weight Paint Mode, 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:
|
||||
@ -275,10 +260,10 @@ For example, calling ``bpy.ops.view3d.render_border()`` from the console raises
|
||||
|
||||
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.
|
||||
In this case the context must be the 3D Viewport with an active camera.
|
||||
|
||||
To avoid using try/except clauses wherever operators are called you can call the operators
|
||||
own ``poll()`` function to check if it can run in the current context.
|
||||
To avoid using try-except clauses wherever operators are called, you can call the operators
|
||||
own ``poll()`` function to check if it can run the operator in the current context.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -291,11 +276,10 @@ Integration
|
||||
|
||||
Python scripts can integrate with Blender in the following ways:
|
||||
|
||||
- By defining a rendering engine.
|
||||
- By defining a render engine.
|
||||
- By defining operators.
|
||||
- By defining menus, headers and panels.
|
||||
- By inserting new buttons into existing 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.
|
||||
|
||||
@ -306,56 +290,52 @@ Example Operator
|
||||
.. literalinclude:: __/__/__/release/scripts/templates_py/operator_simple.py
|
||||
|
||||
Once this script runs, ``SimpleOperator`` is registered with Blender
|
||||
and can be called from the operator search popup or added to the toolbar.
|
||||
and can be called from Operator Search or added to the toolbar.
|
||||
|
||||
To run the script:
|
||||
|
||||
#. Highlight the above code then press :kbd:`Ctrl-C` to copy it.
|
||||
#. Start Blender
|
||||
#. Press :kbd:`Ctrl-Right` twice to change to the Scripting layout.
|
||||
#. Click the button labeled ``New`` and the confirmation pop up in order to create a new text block.
|
||||
#. Press :kbd:`Ctrl-V` to paste the code into the text panel (the upper left frame).
|
||||
#. Click on the button **Run Script**.
|
||||
#. Start Blender and switch to the Scripting workspace.
|
||||
#. Click the *New* button in the text editor to create a new text data-block.
|
||||
#. Copy the code from above and paste it into the text editor.
|
||||
#. Click on the *Run Script* button.
|
||||
#. Move your cursor into the 3D Viewport,
|
||||
open the :ref:`operator search menu <blender_manual:bpy.ops.wm.search_menu>`,
|
||||
open the :ref:`Operator Search menu <blender_manual:bpy.ops.wm.search_menu>`,
|
||||
and type "Simple".
|
||||
#. Click on the "Simple Operator" item found in search.
|
||||
|
||||
.. seealso::
|
||||
|
||||
.. seealso:: The class members with the ``bl_`` prefix are documented in the API
|
||||
reference :class:`bpy.types.Operator`
|
||||
The class members with the ``bl_`` prefix are documented in the API reference :class:`bpy.types.Operator`.
|
||||
|
||||
.. note::
|
||||
|
||||
The output from the ``main`` function is sent to the terminal;
|
||||
in order to see this, be sure to :ref:`use the terminal <use_the_terminal>`.
|
||||
|
||||
|
||||
Example Panel
|
||||
-------------
|
||||
|
||||
Panels register themselves as a class, like an operator.
|
||||
Panels are registered as a class, like an operator.
|
||||
Notice the extra ``bl_`` variables used to set the context they display in.
|
||||
|
||||
.. literalinclude:: __/__/__/release/scripts/templates_py/ui_panel_simple.py
|
||||
|
||||
To run the script:
|
||||
|
||||
#. Highlight the above code then press :kbd:`Ctrl-C` to copy it.
|
||||
#. Start Blender.
|
||||
#. Click on the tab for the *Scripting* workspace.
|
||||
#. Click the button labeled ``New`` to create a new text block.
|
||||
#. Press :kbd:`Ctrl-V` to paste the code into the text panel (the upper left frame).
|
||||
#. Click on the button **Run Script**.
|
||||
|
||||
#. Start Blender and switch to the Scripting workspace.
|
||||
#. Click the *New* button in the text editor to create a new text data-block.
|
||||
#. Copy the code from above and paste it into the text editor.
|
||||
#. Click on the *Run Script* button.
|
||||
|
||||
To view the results:
|
||||
|
||||
#. Select the the default cube.
|
||||
#. Select the default cube.
|
||||
#. Click on the Object properties icon in the buttons panel (far right; appears as a tiny cube).
|
||||
#. Scroll down to see a panel named **Hello World Panel**.
|
||||
#. Changing the object name also updates **Hello World Panel's** Name: field.
|
||||
#. Scroll down to see a panel named "Hello World Panel".
|
||||
#. 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.
|
||||
Note the row distribution and the label and properties that are defined through the code.
|
||||
|
||||
.. seealso:: :class:`bpy.types.Panel`
|
||||
|
||||
@ -364,8 +344,7 @@ 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.
|
||||
Blender's Python API can be split up into three categories.
|
||||
|
||||
|
||||
Native Types
|
||||
@ -374,7 +353,7 @@ 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 float, int, boolean -> float, int, boolean
|
||||
- Blender enumerator -> string
|
||||
|
||||
>>> C.object.rotation_mode = 'AXIS_ANGLE'
|
||||
@ -393,11 +372,10 @@ so these are accessed as normal Python types.
|
||||
Internal Types
|
||||
--------------
|
||||
|
||||
Used for Blender data-blocks and collections: :class:`bpy.types.bpy_struct`
|
||||
:class:`bpy.types.bpy_struct` is used for Blender data-blocks and collections.
|
||||
Also for data that contains its own attributes: collections, meshes, bones, scenes, etc.
|
||||
|
||||
For data that contains its own attributes collections/meshes/bones/scenes... etc.
|
||||
|
||||
There are 2 main types that wrap Blenders data, one for data-blocks
|
||||
There are two main types that wrap Blender's data, one for data-blocks
|
||||
(known internally as ``bpy_struct``), another for properties.
|
||||
|
||||
>>> bpy.context.object
|
||||
@ -406,14 +384,13 @@ There are 2 main types that wrap Blenders data, one for data-blocks
|
||||
>>> C.scene.objects
|
||||
bpy.data.scenes['Scene'].objects
|
||||
|
||||
Note that these types reference Blender's data so modifying them is immediately visible.
|
||||
Note that these types reference Blender's data so modifying them is visible immediately.
|
||||
|
||||
|
||||
Mathutils Types
|
||||
---------------
|
||||
|
||||
Used for vectors, quaternion, eulers, matrix and color types, accessible from :mod:`mathutils`
|
||||
|
||||
Accessible from :mod:`mathutils` are vectors, quaternions, Euler angles, matrix and color types.
|
||||
Some attributes such as :class:`bpy.types.Object.location`,
|
||||
:class:`bpy.types.PoseBone.rotation_euler` and :class:`bpy.types.Scene.cursor_location`
|
||||
can be accessed as special math types which can be used together and manipulated in various useful ways.
|
||||
@ -422,14 +399,13 @@ Example of a matrix, vector multiplication:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
bpy.context.object.matrix_world * bpy.context.object.data.verts[0].co
|
||||
bpy.context.object.matrix_world @ bpy.context.object.data.verts[0].co
|
||||
|
||||
.. note::
|
||||
|
||||
mathutils types keep a reference to Blender's internal data so changes can
|
||||
be applied back.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: python
|
||||
@ -449,13 +425,11 @@ Example of a matrix, vector multiplication:
|
||||
Animation
|
||||
=========
|
||||
|
||||
There are 2 ways to add keyframes through Python.
|
||||
There are two ways to add keyframes through Python.
|
||||
|
||||
The first is through key properties directly, which is similar to inserting a keyframe from the button as a user.
|
||||
The first is through key properties directly, which is like inserting a keyframe from the button 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.
|
||||
|
||||
Both examples insert a keyframe on the active object's Z axis.
|
||||
Here are examples of both methods. Both insert a keyframe on the active object's Z axis.
|
||||
|
||||
Simple example:
|
||||
|
||||
@ -467,7 +441,7 @@ Simple example:
|
||||
obj.location[2] = 1.0
|
||||
obj.keyframe_insert(data_path="location", frame=20.0, index=2)
|
||||
|
||||
Using Low-Level Functions:
|
||||
Using low-level functions:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
|
@ -4,23 +4,22 @@ Tips and Tricks
|
||||
***************
|
||||
|
||||
Here are various suggestions that you might find useful when writing scripts.
|
||||
|
||||
Some of these are just Python features that scripters may not have thought to use with Blender,
|
||||
others are Blender specific.
|
||||
Some of these are just Python features that you may not have thought to use with Blender,
|
||||
others are Blender-specific.
|
||||
|
||||
|
||||
.. _use_the_terminal:
|
||||
|
||||
Use The Terminal
|
||||
Use the Terminal
|
||||
================
|
||||
|
||||
When writing Python scripts, it's useful to have a terminal open,
|
||||
this is not the built-in Python console but a terminal application which is used to start Blender.
|
||||
|
||||
There are 3 main uses for the terminal, these are:
|
||||
The three main use cases for the terminal are:
|
||||
|
||||
- You can see the output of ``print()`` as your script runs, which is useful to view debug info.
|
||||
- The error trace-back is printed in full to the terminal which won't always generate an error popup in
|
||||
- The error traceback is printed in full to the terminal which won't always generate an report message in
|
||||
Blender's user interface (depending on how the script is executed).
|
||||
- If the script runs for too long or you accidentally enter an infinite loop,
|
||||
:kbd:`Ctrl-C` in the terminal (:kbd:`Ctrl-Break` on Windows) will quit the script early.
|
||||
@ -28,26 +27,25 @@ There are 3 main uses for the terminal, these are:
|
||||
.. note::
|
||||
|
||||
For Linux and macOS users this means starting the terminal first, then running Blender from within it.
|
||||
On Windows the terminal can be enabled from the help menu.
|
||||
On Windows the terminal can be enabled from the Help menu.
|
||||
|
||||
|
||||
Interface Tricks
|
||||
================
|
||||
|
||||
|
||||
Access Operator Commands
|
||||
------------------------
|
||||
|
||||
You may have noticed that the tooltip for menu items and buttons includes the ``bpy.ops.[...])`` command
|
||||
You may have noticed that the tooltip for menu items and buttons includes the ``bpy.ops.[...]`` command
|
||||
to run that button, a handy (hidden) feature is that you can press :kbd:`Ctrl-C` over
|
||||
any menu item/button to copy this command into the clipboard.
|
||||
any menu item or button to copy this command into the clipboard.
|
||||
|
||||
|
||||
Access Data Path
|
||||
----------------
|
||||
|
||||
To find the path from an :class:`ID` datablock to its setting isn't always so simple since it may be nested away.
|
||||
To get this quickly you can right click on the setting and select select **Copy Data Path**,
|
||||
To find the path from an :class:`ID` data-block to its setting isn't always so simple since it may be nested away.
|
||||
To get this quickly open the context menu of the setting and select *Copy Data Path*,
|
||||
if this can't be generated, only the property name is copied.
|
||||
|
||||
.. note::
|
||||
@ -62,11 +60,10 @@ if this can't be generated, only the property name is copied.
|
||||
Show All Operators
|
||||
==================
|
||||
|
||||
While Blender logs operators in the Info space,
|
||||
this only reports operators with the ``REGISTER`` option enabeld so as not to flood the *Info* view
|
||||
While Blender logs operators in the Info editor,
|
||||
this only reports operators with the ``REGISTER`` option enabled so as not to flood the *Info* view
|
||||
with calls to ``bpy.ops.view3d.smoothview`` and ``bpy.ops.view3d.zoom``.
|
||||
|
||||
However, for testing it can be useful to see **every** operator called in a terminal,
|
||||
Yet for testing it can be useful to see **every** operator called in a terminal,
|
||||
do this by enabling the debug option either by passing the ``--debug-wm`` argument when starting Blender
|
||||
or by setting :mod:`bpy.app.debug_wm` to ``True`` while Blender is running.
|
||||
|
||||
@ -74,20 +71,18 @@ or by setting :mod:`bpy.app.debug_wm` to ``True`` while Blender is running.
|
||||
Use an External Editor
|
||||
======================
|
||||
|
||||
Blenders text editor is fine for small changes and writing tests but its not full featured,
|
||||
Blender's text editor is fine for small changes and writing tests but its not full featured,
|
||||
for larger projects you'll probably want to use a standalone editor or Python IDE.
|
||||
|
||||
Editing a text file externally and having the same text open in Blender does work but isn't that optimal
|
||||
so here are 2 ways you can easily use an external file from Blender.
|
||||
|
||||
Using the following examples you'll still need textblock in Blender to execute,
|
||||
Editing a text file externally and having the same text open in Blender does work
|
||||
but isn't that optimal so here are two ways you can use an external file from Blender.
|
||||
Using the following examples you'll still need text data-block in Blender to execute,
|
||||
but reference an external file rather than including it directly.
|
||||
|
||||
|
||||
Executing External Scripts
|
||||
--------------------------
|
||||
|
||||
This is the equivalent to running the script directly, referencing a scripts path from a 2 line text-block.
|
||||
This is the equivalent to running the script directly, referencing a scripts path from a two line code block.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -95,7 +90,7 @@ This is the equivalent to running the script directly, referencing a scripts pat
|
||||
exec(compile(open(filename).read(), filename, 'exec'))
|
||||
|
||||
|
||||
You might want to reference a script relative to the blend file.
|
||||
You might want to reference a script relative to the blend-file.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -128,9 +123,10 @@ has to call a function in the module, in this case ``main()`` but it can be any
|
||||
an advantage with this is you can pass arguments to the function from this
|
||||
small script which is often useful for testing different settings quickly.
|
||||
|
||||
The other issue with this is the script has to be in Pythons module search path.
|
||||
While this is not best practice - for testing you can extend the search path,
|
||||
this example adds the current blend files directory to the search path, then loads the script as a module.
|
||||
The other issue with this is the script has to be in Python's module search path.
|
||||
While this is not best practice -- for testing purposes you can extend the search path,
|
||||
this following example adds the current blend-files directory to the search path
|
||||
and then loads the script as a module.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -148,42 +144,40 @@ this example adds the current blend files directory to the search path, then loa
|
||||
myscript.main()
|
||||
|
||||
|
||||
Don't Use Blender!
|
||||
==================
|
||||
|
||||
While developing your own scripts Blenders interface can get in the way,
|
||||
manually reloading, running the scripts, opening file import etc. adds overhead.
|
||||
Use Blender without it's User Interface
|
||||
=======================================
|
||||
|
||||
While developing your own scripts Blender's interface can get in the way,
|
||||
manually reloading, running the scripts, opening file import, etc. adds overhead.
|
||||
For scripts that are not interactive it can end up being more efficient not to use
|
||||
Blenders interface at all and instead execute the script on the command line.
|
||||
Blender's interface at all and instead execute the script on the command line.
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
blender --background --python myscript.py
|
||||
|
||||
|
||||
You might want to run this with a blend file so the script has some data to operate on.
|
||||
You might want to run this with a blend-file so the script has some data to operate on.
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
blender myscene.blend --background --python myscript.py
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
Depending on your setup you might have to enter the full path to the Blender executable.
|
||||
|
||||
|
||||
Once the script is running properly in background mode, you'll want to check the output of the script,
|
||||
this depends completely on the task at hand however here are some suggestions.
|
||||
this depends completely on the task at hand, however, here are some suggestions:
|
||||
|
||||
- render the output to an image, use an image viewer and keep writing over the same image each time.
|
||||
- save a new blend file, or export the file using one of Blenders exporters.
|
||||
- if the results can be displayed as text - print them or write them to a file.
|
||||
- Render the output to an image, use an image viewer and keep writing over the same image each time.
|
||||
- Save a new blend-file, or export the file using one of Blender's exporters.
|
||||
- If the results can be displayed as text then print them or write them to a file.
|
||||
|
||||
|
||||
While this can take a little time to setup, it can be well worth the effort
|
||||
to reduce the time it takes to test changes - you can even have
|
||||
to reduce the time it takes to test changes. You can even have
|
||||
Blender running the script every few seconds with a viewer updating the results,
|
||||
so no need to leave your text editor to see changes.
|
||||
|
||||
@ -200,7 +194,7 @@ but to quickly setup your own custom pipeline or writing one-off scripts this ca
|
||||
|
||||
Examples include:
|
||||
|
||||
- Run The Gimp in batch mode to execute custom scripts for advanced image processing.
|
||||
- Run Gimp in batch mode to execute custom scripts for advanced image processing.
|
||||
- Write out 3D models to use external mesh manipulation tools and read back in the results.
|
||||
- Convert files into recognizable formats before reading.
|
||||
|
||||
@ -209,15 +203,16 @@ Bundled Python & Extensions
|
||||
===========================
|
||||
|
||||
The Blender releases distributed from blender.org include a complete Python installation on all platforms,
|
||||
this has the disadvantage that any extensions you have installed in your systems Python won't be found by Blender.
|
||||
this has the disadvantage that any extensions you have installed on your system's Python environment
|
||||
will not be found by Blender.
|
||||
|
||||
There are 2 ways around this:
|
||||
There are two ways to work around this:
|
||||
|
||||
- Remove Blender Python sub-directory, Blender will then fallback on the systems Python and use that instead.
|
||||
- Remove Blender Python subdirectory, Blender will then fallback on the system's Python and use that instead.
|
||||
|
||||
Depending on your platform,
|
||||
you may need to explicitly reference the location of your Python installation using the
|
||||
``PYTHONPATH`` environment variable, eg:
|
||||
you may need to explicitly reference the location of your Python installation using
|
||||
the ``PYTHONPATH`` environment variable, e.g:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
@ -226,21 +221,21 @@ There are 2 ways around this:
|
||||
.. warning::
|
||||
|
||||
The Python (major, minor) version must match the one that Blender comes with.
|
||||
Therefor can't use Python 3.6 with Blender built to use Python 3.7.
|
||||
Therefor you can't use Python 3.6 with Blender built to use Python 3.7.
|
||||
|
||||
- Copy or link the extensions into Blender's Python sub-directory so Blender can access them,
|
||||
you could also copy the entire Python installation into Blenders sub-directory,
|
||||
- Copy or link the extensions into Blender's Python subdirectory so Blender can access them,
|
||||
you can also copy the entire Python installation into Blender's subdirectory,
|
||||
replacing the one Blender comes with.
|
||||
This works as long as the Python versions match and the paths are created in the same relative locations.
|
||||
Doing this has the advantage that you can redistribute this bundle to others with Blender
|
||||
including any extensions you rely on.
|
||||
|
||||
|
||||
Drop Into a Python Interpreter in Your Script
|
||||
=============================================
|
||||
Insert a Python Interpreter into your Script
|
||||
============================================
|
||||
|
||||
In the middle of a script you may want to inspect some variables,
|
||||
run some function and generally dig about to see what's going on.
|
||||
In the middle of a script you may want to inspect variables,
|
||||
run functions and inspect the flow.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -248,7 +243,7 @@ run some function and generally dig about to see what's going on.
|
||||
code.interact(local=locals())
|
||||
|
||||
|
||||
If you want to access both global and local variables do this...
|
||||
If you want to access both global and local variables run this:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -266,7 +261,7 @@ The next example is an equivalent single line version of the script above which
|
||||
|
||||
|
||||
``code.interact`` can be added at any line in the script
|
||||
and will pause the script an launch an interactive interpreter in the terminal,
|
||||
and will pause the script to launch an interactive interpreter in the terminal,
|
||||
when you're done you can quit the interpreter and the script will continue execution.
|
||||
|
||||
|
||||
@ -279,13 +274,14 @@ The IPython prompt has auto-complete and some useful features that the standard
|
||||
IPython.embed()
|
||||
|
||||
|
||||
Admittedly this highlights the lack of any Python debugging support built into Blender, but its still handy to know.
|
||||
Admittedly this highlights the lack of any Python debugging support built into Blender,
|
||||
but its still a handy thing to know.
|
||||
|
||||
|
||||
Advanced
|
||||
========
|
||||
|
||||
|
||||
Blender as a module
|
||||
Blender as a Module
|
||||
-------------------
|
||||
|
||||
From a Python perspective it's nicer to have everything as an extension
|
||||
@ -293,26 +289,22 @@ which lets the Python script combine many components.
|
||||
|
||||
Advantages include:
|
||||
|
||||
- you can use external editors/IDE's with Blenders Python API and execute scripts within the IDE
|
||||
- You can use external editors or IDEs with Blender's Python API and execute scripts within the IDE
|
||||
(step over code, inspect variables as the script runs).
|
||||
- editors/IDE's can auto complete Blender modules & variables.
|
||||
- existing scripts can import Blender API's without having to run inside Blender.
|
||||
|
||||
- Editors or IDEs can auto-complete Blender modules and variables.
|
||||
- Existing scripts can import Blender APIs without having to be run inside of Blender.
|
||||
|
||||
This is marked advanced because to run Blender as a Python module requires a special build option.
|
||||
|
||||
For instructions on building see
|
||||
`Building Blender as a Python module <https://wiki.blender.org/wiki/Building_Blender/Other/BlenderAsPyModule>`_
|
||||
`Building Blender as a Python module <https://wiki.blender.org/wiki/Building_Blender/Other/BlenderAsPyModule>`__.
|
||||
|
||||
|
||||
Python Safety (Build Option)
|
||||
----------------------------
|
||||
|
||||
Since it's possible to access data which has been removed (see Gotcha's),
|
||||
this can be hard to track down the cause of crashes.
|
||||
|
||||
Since it's possible to access data which has been removed (see :doc:`Gotchas <info_gotcha>`),
|
||||
can make it hard to track down the cause of crashes.
|
||||
To raise Python exceptions on accessing freed data (rather than crashing),
|
||||
enable the CMake build option ``WITH_PYTHON_SAFETY``.
|
||||
|
||||
This enables data tracking which makes data access about 2x slower
|
||||
This enables data tracking which makes data access about two times slower
|
||||
which is why the option isn't enabled in release builds.
|
||||
|
Loading…
Reference in New Issue
Block a user