Add 2 documents to the python api reference.
authorCampbell Barton <ideasman42@gmail.com>
Fri, 7 Dec 2012 05:27:09 +0000 (05:27 +0000)
committerCampbell Barton <ideasman42@gmail.com>
Fri, 7 Dec 2012 05:27:09 +0000 (05:27 +0000)
- 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.

doc/python_api/rst/info_api_reference.rst [new file with mode: 0644]
doc/python_api/rst/info_gotcha.rst
doc/python_api/rst/info_overview.rst
doc/python_api/rst/info_quickstart.rst
doc/python_api/rst/info_tips_and_tricks.rst
doc/python_api/rst/info_tutorial_addon.rst [new file with mode: 0644]
doc/python_api/sphinx_doc_gen.py

diff --git a/doc/python_api/rst/info_api_reference.rst b/doc/python_api/rst/info_api_reference.rst
new file mode 100644 (file)
index 0000000..ddee46d
--- /dev/null
@@ -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>`
+
index a48cb8fc15e4d88051a09f1b694ac8d74d395e2b..34145c2ac492830e9737124851c64c0c087dec6b 100644 (file)
@@ -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
 ===============
 
index 818eb692be9483b16b59b2f842f0aea7edc13fb8..b2d524b74af8d7774270ecd8e49071aae72a3af2 100644 (file)
@@ -1,3 +1,5 @@
+.. _info_overview:
+
 *******************
 Python API Overview
 *******************
index 62ad4e9c4d85c806d43388c5c4e59d018b809f40..e1264ae9d52ce245a45a46a2faf64896aa02fdd5 100644 (file)
@@ -1,3 +1,5 @@
+.. _info_quickstart:
+
 ***********************
 Quickstart Introduction
 ***********************
index 4dcbf431724440c7237c1f5d317429f8d7a4e91f..75e8ef61f6f6c810f8d3dec029afda1a928387d5 100644 (file)
@@ -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
diff --git a/doc/python_api/rst/info_tutorial_addon.rst b/doc/python_api/rst/info_tutorial_addon.rst
new file mode 100644 (file)
index 0000000..2a10104
--- /dev/null
@@ -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*
+
index 1a1a4bf909b0f1b8bf9476f4316b2c4496fee1b6..441a6c04efe795080a763c402bceb41209e153c9 100644 (file)
@@ -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):