Add 2 documents to the python api reference.
- Blender/Python Addon Tutorial: a step by step guide on how to write an addon from scratch - Blender/Python API Reference Usage: examples of how to use the API reference docs Thanks to John Nyquist for editing these docs and giving feedback.
This commit is contained in:
		
							
								
								
									
										305
									
								
								doc/python_api/rst/info_api_reference.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										305
									
								
								doc/python_api/rst/info_api_reference.rst
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,305 @@
 | 
			
		||||
 | 
			
		||||
*******************
 | 
			
		||||
Reference API Usage
 | 
			
		||||
*******************
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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*.
 | 
			
		||||
 | 
			
		||||
Other modules such as :mod:`bge`, :mod:`bmesh` and :mod:`aud` are not using Blenders 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
 | 
			
		||||
relative to them.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
ID Data
 | 
			
		||||
-------
 | 
			
		||||
 | 
			
		||||
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, 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.*``
 | 
			
		||||
- 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.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Simple Data Access
 | 
			
		||||
------------------
 | 
			
		||||
 | 
			
		||||
Lets start with a simple case, say you wan't a python script to adjust the objects location.
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
include examples (normally at the top of the page).
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
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``.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
So now we 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.
 | 
			
		||||
 | 
			
		||||
The other common place to access objects in the reference is :class:`bpy.types.BlendData.objects`.
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
 | 
			
		||||
   This is **not** listed as :mod:`bpy.data.objects`,
 | 
			
		||||
   this is because :mod:`bpy.data` is an instance of the :class:`bpy.types.BlendData` class,
 | 
			
		||||
   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.
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
   bpy.data.objects["Cube"].location
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Nested Properties
 | 
			
		||||
-----------------
 | 
			
		||||
 | 
			
		||||
The previous example is quite straightforward because ``location`` is a property of ``Object`` which can be accessed
 | 
			
		||||
from the context directly.
 | 
			
		||||
 | 
			
		||||
Here are some more complex examples:
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
   # access a render layers samples
 | 
			
		||||
   bpy.context.scene.render.layers["RenderLayer"].samples
 | 
			
		||||
 | 
			
		||||
   # access to the current weight paint brush size
 | 
			
		||||
   bpy.context.tool_settings.weight_paint.brush.size  
 | 
			
		||||
 | 
			
		||||
   # check if the window is fullscreen
 | 
			
		||||
   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 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.
 | 
			
		||||
 | 
			
		||||
- 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>`*
 | 
			
		||||
 | 
			
		||||
- Using the documentation to follow references. *Explained further in :ref:`Indirect Data Access <info_data_path_indirect>`*
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. _info_data_path_copy
 | 
			
		||||
 | 
			
		||||
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,
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
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. 
 | 
			
		||||
 | 
			
		||||
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:
 | 
			
		||||
 | 
			
		||||
.. 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:
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
  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.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. _info_data_path_indirect
 | 
			
		||||
 | 
			
		||||
Indirect Data Access
 | 
			
		||||
--------------------
 | 
			
		||||
 | 
			
		||||
For this example we'll go over something more involved, showing the steps to access the active sculpt brushes texture.
 | 
			
		||||
 | 
			
		||||
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-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 tool-tips).*
 | 
			
		||||
 | 
			
		||||
- The contrast setting isn't exposed in the sculpt toolbar, so view the texture in the properties panel...
 | 
			
		||||
 | 
			
		||||
  - 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 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,
 | 
			
		||||
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:
 | 
			
		||||
 | 
			
		||||
.. 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...
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
   bpy.context.sculpt.brush.texture.contrast
 | 
			
		||||
 | 
			
		||||
Or access the brush directly...
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
   bpy.data.brushes["BrushName"].texture.contrast
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Operators
 | 
			
		||||
=========
 | 
			
		||||
 | 
			
		||||
Most key-strokes 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.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
are interested to check on the source code.
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
 | 
			
		||||
   Not all operators can be called usefully from Python, for more on this see :ref:`using operators <using_operators>`.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Info View
 | 
			
		||||
---------
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
.. 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>`
 | 
			
		||||
 | 
			
		||||
@@ -5,6 +5,8 @@ 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.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. _using_operators:
 | 
			
		||||
 | 
			
		||||
Using Operators
 | 
			
		||||
===============
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
.. _info_overview:
 | 
			
		||||
 | 
			
		||||
*******************
 | 
			
		||||
Python API Overview
 | 
			
		||||
*******************
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
.. _info_quickstart:
 | 
			
		||||
 | 
			
		||||
***********************
 | 
			
		||||
Quickstart Introduction
 | 
			
		||||
***********************
 | 
			
		||||
 
 | 
			
		||||
@@ -44,15 +44,17 @@ if this can't be generated, only the property name is copied.
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
 | 
			
		||||
   This uses the same method for creating the animation path used by :class:`FCurve.data_path` and :class:`DriverTarget.data_path` drivers.
 | 
			
		||||
   This uses the same method for creating the animation path used by :class:`bpy.types.FCurve.data_path` and :class:`bpy.types.DriverTarget.data_path` drivers.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. _info_show_all_operators
 | 
			
		||||
 | 
			
		||||
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 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, do this by enabling the debug option either by passing the ``--debug`` argument when starting blender or by setting :mod:`bpy.app.debug` to True while blender is running.
 | 
			
		||||
However, 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.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Use an External Editor
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										645
									
								
								doc/python_api/rst/info_tutorial_addon.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										645
									
								
								doc/python_api/rst/info_tutorial_addon.rst
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,645 @@
 | 
			
		||||
 | 
			
		||||
Addon Tutorial
 | 
			
		||||
##############
 | 
			
		||||
 | 
			
		||||
************
 | 
			
		||||
Introduction
 | 
			
		||||
************
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Intended Audience
 | 
			
		||||
=================
 | 
			
		||||
 | 
			
		||||
This tutorial is designed to help technical artists or developers learn to extend Blender.
 | 
			
		||||
An understanding of the basics of Python is expected for those working through this tutorial.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Prerequisites
 | 
			
		||||
-------------
 | 
			
		||||
 | 
			
		||||
Before going through the tutorial you should...
 | 
			
		||||
 | 
			
		||||
* Familiarity with the basics of working in Blender.
 | 
			
		||||
 | 
			
		||||
* Know how to run a script in Blender's text editor (as documented in the quick-start)
 | 
			
		||||
 | 
			
		||||
* Have an understanding of Python primitive types (int, boolean, string, list, tuple, dictionary, and set).
 | 
			
		||||
 | 
			
		||||
* Be familiar with the concept of Python modules.
 | 
			
		||||
 | 
			
		||||
* Basic understanding of classes (object orientation) in Python.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Suggested reading before starting this tutorial.
 | 
			
		||||
 | 
			
		||||
* `Dive Into Python <http://getpython3.com/diveintopython3/index.html>`_ sections (1, 2, 3, 4, and 7).
 | 
			
		||||
* :ref:`Blender API Quickstart <info_quickstart>`
 | 
			
		||||
  to help become familiar with Blender/Python basics.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
To best troubleshoot any error message Python prints while writing scripts you run blender with from a terminal,
 | 
			
		||||
see :ref:`Use The Terminal <use_the_terminal>`.
 | 
			
		||||
 | 
			
		||||
Documentation Links
 | 
			
		||||
===================
 | 
			
		||||
 | 
			
		||||
While going through the tutorial you may want to look into our reference documentation.
 | 
			
		||||
 | 
			
		||||
* :ref:`Blender API Overview <info_overview>`. -
 | 
			
		||||
  *This document is rather detailed but helpful if you want to know more on a topic.*
 | 
			
		||||
 | 
			
		||||
* :mod:`bpy.context` api reference. -
 | 
			
		||||
  *Handy to have a list of available items your script may operate on.*
 | 
			
		||||
 | 
			
		||||
* :class:`bpy.types.Operator`. -
 | 
			
		||||
  *The following addons define operators, these docs give details and more examples of operators.*
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
******
 | 
			
		||||
Addons
 | 
			
		||||
******
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
What is an Addon?
 | 
			
		||||
=================
 | 
			
		||||
 | 
			
		||||
An addon is simply a Python module with some additional requirements so Blender can display it in a list with useful
 | 
			
		||||
information.
 | 
			
		||||
 | 
			
		||||
To give an example, here is the simplest possible addon.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
   bl_info = {"name": "My Test Addon", "category": "Object"}
 | 
			
		||||
   def register():
 | 
			
		||||
       print("Hello World")
 | 
			
		||||
   def unregister():
 | 
			
		||||
       print("Goodbye World")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
* ``bl_info`` is a dictionary containing addon meta-data such as the title, version and author to be displayed in the
 | 
			
		||||
  user preferences addon list.
 | 
			
		||||
* ``register`` is a function which only runs when enabling the addon, this means the module can be loaded without
 | 
			
		||||
  activating the addon.
 | 
			
		||||
* ``unregister`` is a function to unload anything setup by ``register``, this is called when the addon is disabled.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Notice this addon does not do anything related to Blender, (the :mod:`bpy` module is not imported for example).
 | 
			
		||||
 | 
			
		||||
This is a contrived example of an addon that serves to illustrate the point
 | 
			
		||||
that the base requirements of an addon are simple.
 | 
			
		||||
 | 
			
		||||
An addon will typically register operators, panels, menu items etc, but its worth noting that _any_ script can do this,
 | 
			
		||||
when executed from the text editor or even the interactive console - there is nothing inherently different about an
 | 
			
		||||
addon that allows it to integrate with Blender, such functionality is just provided by the :mod:`bpy` module for any
 | 
			
		||||
script to access.
 | 
			
		||||
 | 
			
		||||
So an addon is just a way to encapsulate a Python module in a way a user can easily utilize.
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
 | 
			
		||||
   Running this script within the text editor won't print anything,
 | 
			
		||||
   to see the output it must be installed through the user preferences.
 | 
			
		||||
   Messages will be printed when enabling and disabling.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Your First Addon
 | 
			
		||||
================
 | 
			
		||||
 | 
			
		||||
The simplest possible addon above was useful as an example but not much else.
 | 
			
		||||
This next addon is simple but shows how to integrate a script into Blender using an ``Operator``
 | 
			
		||||
which is the typical way to define a tool accessed from menus, buttons and keyboard shortcuts.
 | 
			
		||||
 | 
			
		||||
For the first example we'll make a script that simply moves all objects in a scene.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Write The Script
 | 
			
		||||
----------------
 | 
			
		||||
 | 
			
		||||
Add the following script to the text editor in Blender.
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
   import bpy
 | 
			
		||||
 | 
			
		||||
   scene = bpy.context.scene
 | 
			
		||||
   for obj in scene.objects:
 | 
			
		||||
       obj.location.x += 1.0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. image:: run_script.png
 | 
			
		||||
   :width: 924px
 | 
			
		||||
   :align: center
 | 
			
		||||
   :height: 574px
 | 
			
		||||
   :alt: Run Script button
 | 
			
		||||
 | 
			
		||||
Click the Run Script button, all objects in the active scene are moved by 1.0 Blender unit.
 | 
			
		||||
Next we'll make this script into an addon.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Write the Addon (Simple)
 | 
			
		||||
------------------------
 | 
			
		||||
 | 
			
		||||
This addon takes the body of the script above, and adds them to an operator's ``execute()`` function.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
   bl_info = {
 | 
			
		||||
       "name": "Move X Axis",
 | 
			
		||||
       "category": "Object",
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   import bpy
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
   class ObjectMoveX(bpy.types.Operator):
 | 
			
		||||
       """My Object Moving Script"""      # blender will use this as a tooltip for menu items and buttons.
 | 
			
		||||
       bl_idname = "object.move_x"        # unique identifier for buttons and menu items to reference.
 | 
			
		||||
       bl_label = "Move X by One"         # display name in the interface.
 | 
			
		||||
       bl_options = {'REGISTER', 'UNDO'}  # enable undo for the operator.
 | 
			
		||||
 | 
			
		||||
       def execute(self, context):        # execute() is called by blender when running the operator.
 | 
			
		||||
 | 
			
		||||
           # The original script
 | 
			
		||||
           scene = context.scene
 | 
			
		||||
           for obj in scene.objects:
 | 
			
		||||
               obj.location.x += 1.0
 | 
			
		||||
 | 
			
		||||
           return {'FINISHED'}            # this lets blender know the operator finished successfully.
 | 
			
		||||
 | 
			
		||||
   def register():
 | 
			
		||||
       bpy.utils.register_class(ObjectMoveX)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
   def unregister():
 | 
			
		||||
       bpy.utils.unregister_class(ObjectMoveX)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
   # This allows you to run the script directly from blenders text editor
 | 
			
		||||
   # to test the addon without having to install it.
 | 
			
		||||
   if __name__ == "__main__":
 | 
			
		||||
       register()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. note:: ``bl_info`` is split across multiple lines, this is just a style convention used to more easily add items.
 | 
			
		||||
 | 
			
		||||
.. note:: Rather than using ``bpy.context.scene``, we use the ``context.scene`` argument passed to ``execute()``.
 | 
			
		||||
          In most cases these will be the same however in some cases operators will be passed a custom context
 | 
			
		||||
          so script authors should prefer the ``context`` argument passed to operators.
 | 
			
		||||
   
 | 
			
		||||
 | 
			
		||||
To test the script you can copy and paste this into Blender text editor and run it, this will execute the script
 | 
			
		||||
directly and call register immediately.
 | 
			
		||||
 | 
			
		||||
However running the script wont move any objects, for this you need to execute the newly registered operator.
 | 
			
		||||
 | 
			
		||||
.. image:: spacebar.png
 | 
			
		||||
   :width: 924px
 | 
			
		||||
   :align: center
 | 
			
		||||
   :height: 574px
 | 
			
		||||
   :alt: Spacebar
 | 
			
		||||
 | 
			
		||||
Do this by pressing ``SpaceBar`` to bring up the operator search dialog and type in "Move X by One" (the ``bl_label``),
 | 
			
		||||
then press ``Enter``.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
The objects should move as before.
 | 
			
		||||
 | 
			
		||||
*Keep this addon open in Blender for the next step - Installing.*
 | 
			
		||||
 | 
			
		||||
Install The Addon
 | 
			
		||||
-----------------
 | 
			
		||||
 | 
			
		||||
Once you have your addon within in Blender's text editor, you will want to be able to install it so it can be enabled in
 | 
			
		||||
the user preferences to load on startup.
 | 
			
		||||
 | 
			
		||||
Even though the addon above is a test, lets go through the steps anyway so you know how to do it for later.
 | 
			
		||||
 | 
			
		||||
To install the Blender text as an addon you will first have to save it to disk, take care to obey the naming
 | 
			
		||||
restrictions that apply to Python modules and end with a ``.py`` extension.
 | 
			
		||||
 | 
			
		||||
Once the file is on disk, you can install it as you would for an addon downloaded online.
 | 
			
		||||
 | 
			
		||||
Open the user **File -> User Preferences**, Select the **Addon** section, press **Install Addon...** and select the file. 
 | 
			
		||||
 | 
			
		||||
Now the addon will be listed and you can enable it by pressing the check-box, if you want it to be enabled on restart,
 | 
			
		||||
press **Save as Default**.
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
 | 
			
		||||
   The destination of the addon depends on your Blender configuration.
 | 
			
		||||
   When installing an addon the source and destination path are printed in the console.
 | 
			
		||||
   You can also find addon path locations by running this in the Python console.
 | 
			
		||||
 | 
			
		||||
   .. code-block:: python
 | 
			
		||||
 | 
			
		||||
      import addon_utils
 | 
			
		||||
      print(addon_utils.paths())
 | 
			
		||||
 | 
			
		||||
   More is written on this topic here:
 | 
			
		||||
   `Directory Layout <http://wiki.blender.org/index.php/Doc:2.6/Manual/Introduction/Installing_Blender/DirectoryLayout>`_
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Your Second Addon
 | 
			
		||||
=================
 | 
			
		||||
 | 
			
		||||
For our second addon, we will focus on object instancing - this is - to make linked copies of an object in a
 | 
			
		||||
similar way to what you may have seen with the array modifier.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Write The Script
 | 
			
		||||
----------------
 | 
			
		||||
 | 
			
		||||
As before, first we will start with a script, develop it, then convert into an addon.
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
   import bpy
 | 
			
		||||
   from bpy import context
 | 
			
		||||
 | 
			
		||||
   # Get the current scene
 | 
			
		||||
   scene = context.scene
 | 
			
		||||
 | 
			
		||||
   # Get the 3D cursor
 | 
			
		||||
   cursor = scene.cursor_location
 | 
			
		||||
 | 
			
		||||
   # Get the active object (assume we have one)
 | 
			
		||||
   obj = scene.objects.active
 | 
			
		||||
 | 
			
		||||
   # Now make a copy of the object
 | 
			
		||||
   obj_new = obj.copy()
 | 
			
		||||
 | 
			
		||||
   # The object won't automatically get into a new scene
 | 
			
		||||
   scene.objects.link(obj_new)
 | 
			
		||||
 | 
			
		||||
   # Now we can place the object
 | 
			
		||||
   obj_new.location = cursor
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Now try copy this script into Blender and run it on the default cube.
 | 
			
		||||
Make sure you click to move the 3D cursor before running as the duplicate will appear at the cursor's location.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
... go off and test ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
After running, notice that when you go into edit-mode to change the cube - all of the copies change,
 | 
			
		||||
in Blender this is known as *Linked-Duplicates*.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Next, we're going to do this in a loop, to make an array of objects between the active object and the cursor.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
   import bpy
 | 
			
		||||
   from bpy import context
 | 
			
		||||
 | 
			
		||||
   scene = context.scene
 | 
			
		||||
   cursor = scene.cursor_location
 | 
			
		||||
   obj = scene.objects.active
 | 
			
		||||
 | 
			
		||||
   # Use a fixed value for now, eventually make this user adjustable
 | 
			
		||||
   total = 10
 | 
			
		||||
 | 
			
		||||
   # Add 'total' objects into the scene
 | 
			
		||||
   for i in range(total):
 | 
			
		||||
       obj_new = obj.copy()
 | 
			
		||||
       scene.objects.link(obj_new)
 | 
			
		||||
 | 
			
		||||
       # Now place the object in between the cursor
 | 
			
		||||
       # and the active object based on 'i'
 | 
			
		||||
       factor = i / total
 | 
			
		||||
       obj_new.location = (obj.location * factor) + (cursor * (1.0 - factor))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Try run this script with with the active object and the cursor spaced apart to see the result.
 | 
			
		||||
 | 
			
		||||
With this script you'll notice we're doing some math with the object location and cursor, this works because both are
 | 
			
		||||
3D :class:`mathutils.Vector` instances, a convenient class provided by the :mod:`mathutils` module and
 | 
			
		||||
allows vectors to be multiplied by numbers and matrices.
 | 
			
		||||
 | 
			
		||||
If you are interested in this area, read into :class:`mathutils.Vector` - there are many handy utility functions
 | 
			
		||||
such as getting the angle between vectors, cross product, dot products
 | 
			
		||||
as well as more advanced functions in :mod:`mathutils.geometry` such as bezier spline interpolation and
 | 
			
		||||
ray-triangle intersection.
 | 
			
		||||
 | 
			
		||||
For now we'll focus on making this script an addon, but its good to know that this 3D math module is available and
 | 
			
		||||
can help you with more advanced functionality later on.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Write the Addon
 | 
			
		||||
---------------
 | 
			
		||||
 | 
			
		||||
The first step is to convert the script as-is into an addon.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
   bl_info = {
 | 
			
		||||
       "name": "Cursor Array",
 | 
			
		||||
       "category": "Object",
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   import bpy
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
   class ObjectCursorArray(bpy.types.Operator):
 | 
			
		||||
       """Object Cursor Array"""
 | 
			
		||||
       bl_idname = "object.cursor_array"
 | 
			
		||||
       bl_label = "Cursor Array"
 | 
			
		||||
       bl_options = {'REGISTER', 'UNDO'}
 | 
			
		||||
 | 
			
		||||
       def execute(self, context):
 | 
			
		||||
           scene = context.scene
 | 
			
		||||
           cursor = scene.cursor_location
 | 
			
		||||
           obj = scene.objects.active
 | 
			
		||||
 | 
			
		||||
           total = 10
 | 
			
		||||
 | 
			
		||||
           for i in range(total):
 | 
			
		||||
               obj_new = obj.copy()
 | 
			
		||||
               scene.objects.link(obj_new)
 | 
			
		||||
 | 
			
		||||
               factor = i / total
 | 
			
		||||
               obj_new.location = (obj.location * factor) + (cursor * (1.0 - factor))
 | 
			
		||||
 | 
			
		||||
           return {'FINISHED'}
 | 
			
		||||
 | 
			
		||||
   def register():
 | 
			
		||||
       bpy.utils.register_class(ObjectCursorArray)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
   def unregister():
 | 
			
		||||
       bpy.utils.unregister_class(ObjectCursorArray)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
   if __name__ == "__main__":
 | 
			
		||||
       register()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Everything here has been covered in the previous steps, you may want to try run the addon still
 | 
			
		||||
and consider what could be done to make it more useful.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
... go off and test ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
The two of the most obvious missing things are - having the total fixed at 10, and having to access the operator from
 | 
			
		||||
space-bar is not very convenient.
 | 
			
		||||
 | 
			
		||||
Both these additions are explained next, with the final script afterwards.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Operator Property
 | 
			
		||||
^^^^^^^^^^^^^^^^^
 | 
			
		||||
 | 
			
		||||
There are a variety of property types that are used for tool settings, common property types include:
 | 
			
		||||
int, float, vector, color, boolean and string.
 | 
			
		||||
 | 
			
		||||
These properties are handled differently to typical Python class attributes
 | 
			
		||||
because Blender needs to be display them in the interface,
 | 
			
		||||
store their settings in key-maps and keep settings for re-use.
 | 
			
		||||
 | 
			
		||||
While this is handled in a fairly Pythonic way, be mindful that you are in fact defining tool settings that
 | 
			
		||||
are loaded into Blender and accessed by other parts of Blender, outside of Python.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
To get rid of the literal 10 for `total`, we'll us an operator property.
 | 
			
		||||
Operator properties are defined via bpy.props module, this is added to the class body.
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
   # moved assignment from execute() to the body of the class...
 | 
			
		||||
   total = bpy.props.IntProperty(name="Steps", default=2, min=1, max=100)
 | 
			
		||||
 | 
			
		||||
   # and this is accessed on the class
 | 
			
		||||
   # instance within the execute() function as...
 | 
			
		||||
   self.total
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
These properties from :mod:`bpy.props` are handled specially by Blender when the class is registered
 | 
			
		||||
so they display as buttons in the user interface.
 | 
			
		||||
There are many arguments you can pass to properties to set limits, change the default and display a tooltip.
 | 
			
		||||
 | 
			
		||||
.. seealso:: :mod:`bpy.props.IntProperty`
 | 
			
		||||
 | 
			
		||||
This document doesn't go into details about using other property types,
 | 
			
		||||
however the link above includes examples of more advanced property usage.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Menu Item
 | 
			
		||||
^^^^^^^^^
 | 
			
		||||
 | 
			
		||||
Addons can add to the user interface of existing panels, headers and menus defined in Python.
 | 
			
		||||
 | 
			
		||||
For this example we'll add to an existing menu.
 | 
			
		||||
 | 
			
		||||
.. image:: menu_id.png
 | 
			
		||||
   :width: 334px
 | 
			
		||||
   :align: center
 | 
			
		||||
   :height: 128px
 | 
			
		||||
   :alt: Menu Identifier
 | 
			
		||||
 | 
			
		||||
To find the identifier of a menu you can hover your mouse over the menu item and the identifier is displayed.
 | 
			
		||||
 | 
			
		||||
The method used for adding a menu item is to append a draw function into an existing class.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
   def menu_func(self, context):
 | 
			
		||||
       self.layout.operator(ObjectCursorArray.bl_idname)
 | 
			
		||||
 | 
			
		||||
   def register():
 | 
			
		||||
       bpy.types.VIEW3D_MT_object.append(menu_func)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
For docs on extending menus see: :doc:`bpy.types.Menu`.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Keymap
 | 
			
		||||
^^^^^^
 | 
			
		||||
 | 
			
		||||
In Blender addons have their own key-maps so as not to interfere with Blenders built in key-maps.
 | 
			
		||||
 | 
			
		||||
In the example below, a new object-mode :class:`bpy.types.KeyMap` is added,
 | 
			
		||||
then a :class:`bpy.types.KeyMapItem` is added to the key-map which references our newly added operator,
 | 
			
		||||
using :kbd:`Ctrl-Shift-Space` as the key shortcut to activate it.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
   # store keymaps here to access after registration
 | 
			
		||||
   addon_keymaps = []
 | 
			
		||||
 | 
			
		||||
   def register():
 | 
			
		||||
 | 
			
		||||
       # handle the keymap
 | 
			
		||||
       wm = bpy.context.window_manager
 | 
			
		||||
       km = wm.keyconfigs.addon.keymaps.new(name='Object Mode', space_type='EMPTY')
 | 
			
		||||
 | 
			
		||||
       kmi = km.keymap_items.new(ObjectCursorArray.bl_idname, 'SPACE', 'PRESS', ctrl=True, shift=True)
 | 
			
		||||
       kmi.properties.total = 4
 | 
			
		||||
 | 
			
		||||
       addon_keymaps.append(km)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
   def unregister():
 | 
			
		||||
 | 
			
		||||
       # handle the keymap
 | 
			
		||||
       wm = bpy.context.window_manager
 | 
			
		||||
       for km in addon_keymaps:
 | 
			
		||||
           wm.keyconfigs.addon.keymaps.remove(km)
 | 
			
		||||
       # clear the list
 | 
			
		||||
       addon_keymaps.clear()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Notice how the key-map item can have a different ``total`` setting then the default set by the operator,
 | 
			
		||||
this allows you to have multiple keys accessing the same operator with different settings.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
 | 
			
		||||
   While :kbd:`Ctrl-Shift-Space` isn't a default Blender key shortcut, its hard to make sure addons won't
 | 
			
		||||
   overwrite each others keymaps, At least take care when assigning keys that they don't
 | 
			
		||||
   conflict with important functionality within Blender.
 | 
			
		||||
 | 
			
		||||
For API documentation on the functions listed above, see:
 | 
			
		||||
:class:`bpy.types.KeyMaps.new`,
 | 
			
		||||
:class:`bpy.types.KeyMap`,
 | 
			
		||||
:class:`bpy.types.KeyMapItems.new`,
 | 
			
		||||
:class:`bpy.types.KeyMapItem`.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Bringing it all together
 | 
			
		||||
^^^^^^^^^^^^^^^^^^^^^^^^
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
   bl_info = {
 | 
			
		||||
       "name": "Cursor Array",
 | 
			
		||||
       "category": "Object",
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   import bpy
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
   class ObjectCursorArray(bpy.types.Operator):
 | 
			
		||||
       """Object Cursor Array"""
 | 
			
		||||
       bl_idname = "object.cursor_array"
 | 
			
		||||
       bl_label = "Cursor Array"
 | 
			
		||||
       bl_options = {'REGISTER', 'UNDO'}
 | 
			
		||||
 | 
			
		||||
       total = bpy.props.IntProperty(name="Steps", default=2, min=1, max=100)
 | 
			
		||||
 | 
			
		||||
       def execute(self, context):
 | 
			
		||||
           scene = context.scene
 | 
			
		||||
           cursor = scene.cursor_location
 | 
			
		||||
           obj = scene.objects.active
 | 
			
		||||
 | 
			
		||||
           for i in range(self.total):
 | 
			
		||||
               obj_new = obj.copy()
 | 
			
		||||
               scene.objects.link(obj_new)
 | 
			
		||||
 | 
			
		||||
               factor = i / self.total
 | 
			
		||||
               obj_new.location = (obj.location * factor) + (cursor * (1.0 - factor))
 | 
			
		||||
 | 
			
		||||
           return {'FINISHED'}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
   def menu_func(self, context):
 | 
			
		||||
       self.layout.operator(ObjectCursorArray.bl_idname)
 | 
			
		||||
 | 
			
		||||
   # store keymaps here to access after registration
 | 
			
		||||
   addon_keymaps = []
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
   def register():
 | 
			
		||||
       bpy.utils.register_class(ObjectCursorArray)
 | 
			
		||||
       bpy.types.VIEW3D_MT_object.append(menu_func)
 | 
			
		||||
 | 
			
		||||
       # handle the keymap
 | 
			
		||||
       wm = bpy.context.window_manager
 | 
			
		||||
       km = wm.keyconfigs.addon.keymaps.new(name='Object Mode', space_type='EMPTY')
 | 
			
		||||
       kmi = km.keymap_items.new(ObjectCursorArray.bl_idname, 'SPACE', 'PRESS', ctrl=True, shift=True)
 | 
			
		||||
       kmi.properties.total = 4
 | 
			
		||||
       addon_keymaps.append(km)
 | 
			
		||||
 | 
			
		||||
   def unregister():
 | 
			
		||||
       bpy.utils.unregister_class(ObjectCursorArray)
 | 
			
		||||
       bpy.types.VIEW3D_MT_object.remove(menu_func)
 | 
			
		||||
 | 
			
		||||
       # handle the keymap
 | 
			
		||||
       wm = bpy.context.window_manager
 | 
			
		||||
       for km in addon_keymaps:
 | 
			
		||||
           wm.keyconfigs.addon.keymaps.remove(km)
 | 
			
		||||
       # clear the list
 | 
			
		||||
       del addon_keymaps[:]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
   if __name__ == "__main__":
 | 
			
		||||
       register()
 | 
			
		||||
 | 
			
		||||
.. image:: in_menu.png
 | 
			
		||||
   :width: 591px
 | 
			
		||||
   :align: center
 | 
			
		||||
   :height: 649px
 | 
			
		||||
   :alt: In the menu
 | 
			
		||||
 | 
			
		||||
Run the script (or save it and add it through the Preferences like before) and it will appear in the menu.
 | 
			
		||||
 | 
			
		||||
.. image:: op_prop.png
 | 
			
		||||
   :width: 669px
 | 
			
		||||
   :align: center
 | 
			
		||||
   :height: 644px
 | 
			
		||||
   :alt: Operator Property
 | 
			
		||||
 | 
			
		||||
After selecting it from the menu, you can choose how many instance of the cube you want created.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
 | 
			
		||||
   Directly executing the script multiple times will add the menu each time too.
 | 
			
		||||
   While not useful behavior, theres nothing to worry about since addons won't register them selves multiple
 | 
			
		||||
   times when enabled through the user preferences.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Conclusions
 | 
			
		||||
===========
 | 
			
		||||
 | 
			
		||||
Addons can encapsulate certain functionality neatly for writing tools to improve your work-flow or for writing utilities
 | 
			
		||||
for others to use.
 | 
			
		||||
 | 
			
		||||
While there are limits to what Python can do within Blender, there is certainly a lot that can be achieved without
 | 
			
		||||
having to dive into Blender's C/C++ code.
 | 
			
		||||
 | 
			
		||||
The example given in the tutorial is limited, but shows the Blender API used for common tasks that you can expand on
 | 
			
		||||
to write your own tools.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Further Reading
 | 
			
		||||
---------------
 | 
			
		||||
 | 
			
		||||
Blender comes commented templates which are accessible from the text editor header, if you have specific areas
 | 
			
		||||
you want to see example code for, this is a good place to start.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Here are some sites you might like to check on after completing this tutorial.
 | 
			
		||||
 | 
			
		||||
* :ref:`Blender/Python API Overview <info_overview>` -
 | 
			
		||||
  *For more background details on Blender/Python integration.*
 | 
			
		||||
 | 
			
		||||
* `How to Think Like a Computer Scientist <http://interactivepython.org/courselib/static/thinkcspy/index.html>`_ -
 | 
			
		||||
  *Great info for those who are still learning Python.*
 | 
			
		||||
 | 
			
		||||
* `Blender Development (Wiki) <http://wiki.blender.org/index.php/Dev:Contents>`_ -
 | 
			
		||||
  *Blender Development, general information and helpful links.*
 | 
			
		||||
 | 
			
		||||
* `Blender Artists (Coding Section) <http://blenderartists.org/forum/forumdisplay.php?47-Coding>`_ -
 | 
			
		||||
  *forum where people ask Python development questions*
 | 
			
		||||
 | 
			
		||||
@@ -316,6 +316,8 @@ RST_DIR = os.path.abspath(os.path.join(SCRIPT_DIR, "rst"))
 | 
			
		||||
INFO_DOCS = (
 | 
			
		||||
    ("info_quickstart.rst", "Blender/Python Quickstart: new to blender/scripting and want to get your feet wet?"),
 | 
			
		||||
    ("info_overview.rst", "Blender/Python API Overview: a more complete explanation of python integration"),
 | 
			
		||||
    ("info_tutorial_addon.rst", "Blender/Python Addon Tutorial: a step by step guide on how to write an addon from scratch"),
 | 
			
		||||
    ("info_api_reference.rst", "Blender/Python API Reference Usage: examples of how to use the API reference docs"),
 | 
			
		||||
    ("info_best_practice.rst", "Best Practice: Conventions to follow for writing good scripts"),
 | 
			
		||||
    ("info_tips_and_tricks.rst", "Tips and Tricks: Hints to help you while writing scripts for blender"),
 | 
			
		||||
    ("info_gotcha.rst", "Gotcha's: some of the problems you may come up against when writing scripts"),
 | 
			
		||||
@@ -1724,6 +1726,11 @@ def copy_handwritten_rsts(basepath):
 | 
			
		||||
    # changelog
 | 
			
		||||
    shutil.copy2(os.path.join(RST_DIR, "change_log.rst"), basepath)
 | 
			
		||||
 | 
			
		||||
    # copy images, could be smarter but just glob for now.
 | 
			
		||||
    for f in os.listdir(RST_DIR):
 | 
			
		||||
        if f.endswith(".png"):
 | 
			
		||||
            shutil.copy2(os.path.join(RST_DIR, f), basepath)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def rna2sphinx(basepath):
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user