diff options
author | Aaron Carlisle <carlisle.b3d@gmail.com> | 2017-06-15 20:00:58 +0300 |
---|---|---|
committer | Aaron Carlisle <carlisle.b3d@gmail.com> | 2017-06-16 05:01:03 +0300 |
commit | 127a754615efff02669a9afb50c2d44f6b354403 (patch) | |
tree | 9032331459099289632042e4c7f79fb999bcf44a /doc | |
parent | 6b502731a50d22d8d2b4efb81908ec0616a66d5d (diff) |
PyAPI: Remove addon tutorial
This page now lives at https://docs.blender.org/manual/en/dev/advanced/scripting/addon_tutorial.html
Diffstat (limited to 'doc')
-rw-r--r-- | doc/python_api/rst/info_tutorial_addon.rst | 635 | ||||
-rw-r--r-- | doc/python_api/sphinx_doc_gen.py | 5 |
2 files changed, 3 insertions, 637 deletions
diff --git a/doc/python_api/rst/info_tutorial_addon.rst b/doc/python_api/rst/info_tutorial_addon.rst deleted file mode 100644 index 92fbf9b8787..00000000000 --- a/doc/python_api/rst/info_tutorial_addon.rst +++ /dev/null @@ -1,635 +0,0 @@ - -Add-on 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 add-ons define operators, these docs give details and more examples of operators.* - - -******* -Add-ons -******* - -What is an Add-on? -================== - -An add-on 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 add-on. - -.. code-block:: python - - bl_info = {"name": "My Test Add-on", "category": "Object"} - def register(): - print("Hello World") - def unregister(): - print("Goodbye World") - - -- ``bl_info`` is a dictionary containing add-on metadata such as the title, - version and author to be displayed in the user preferences add-on list. -- ``register`` is a function which only runs when enabling the add-on, - this means the module can be loaded without activating the add-on. -- ``unregister`` is a function to unload anything setup by ``register``, this is called when the add-on is disabled. - - -Notice this add-on does not do anything related to Blender, (the :mod:`bpy` module is not imported for example). - -This is a contrived example of an add-on that serves to illustrate the point -that the base requirements of an add-on are simple. - -An add-on 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 -add-on that allows it to integrate with Blender, such functionality is just provided by the :mod:`bpy` module for any -script to access. - -So an add-on 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 Add-on -================= - -The simplest possible add-on above is useful as an example but not much else. -This next add-on 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 will 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 - - -Click the :ref:`Run Script button <blender_manual:editors-text-run-script>`, -all objects in the active scene are moved by 1.0 Blender unit. - - -Write the Add-on (Simple) -------------------------- - -This add-on 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 add-on 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 :kbd:`Spacebar` to bring up the operator search dialog and type in -"Move X by One" (the ``bl_label``), then :kbd:`Enter`. - - - -The objects should move as before. - -*Keep this add-on open in Blender for the next step - Installing.* - - -Install The Add-on ------------------- - -Once you have your add-on 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 add-on 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 add-on 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 add-on downloaded online. - -Open the user :menuselection:`File --> User Preferences`, -Select the *Add-on* section, press *Install Add-on...* and select the file. - -Now the add-on 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 add-on depends on your Blender configuration. - When installing an add-on the source and destination path are printed in the console. - You can also find add-on 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: - :ref:`Directory Layout <blender_manual:getting-started_installing-config-directories>`. - - -Your Second Add-on -================== - -For our second add-on, 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 add-on. - -.. 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 Bézier Spline interpolation and -ray-triangle intersection. - -For now we will focus on making this script an add-on, but its good to know that this 3D math module is available and -can help you with more advanced functionality later on. - - -Write the Add-on ----------------- - -The first step is to convert the script as-is into an add-on. - - -.. 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 add-on 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 -^^^^^^^^^ - -Add-ons 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, add-ons have their own keymaps 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, kmi)) - - - def unregister(): - - # handle the keymap - for km, kmi in addon_keymaps: - km.keymap_items.remove(kmi) - 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 add-ons 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 - # Note that in background mode (no GUI available), keyconfigs are not available either, so we have to check this - # to avoid nasty errors in background case. - kc = wm.keyconfigs.addon - if kc: - 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, kmi)) - - def unregister(): - # Note: when unregistering, it's usually good practice to do it in reverse order you registered. - # Can avoid strange issues like keymap still referring to operators already unregistered... - # handle the keymap - for km, kmi in addon_keymaps: - km.keymap_items.remove(kmi) - addon_keymaps.clear() - - bpy.utils.unregister_class(ObjectCursorArray) - bpy.types.VIEW3D_MT_object.remove(menu_func) - - - 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 add-ons won't register them selves multiple - times when enabled through the user preferences. - - -Conclusions -=========== - -Add-ons 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) <https://wiki.blender.org/index.php/Dev:Contents>`_ - - *Blender Development, general information and helpful links.* -- `Blender Artists (Coding Section) <https://blenderartists.org/forum/forumdisplay.php?47-Coding>`_ - - *forum where people ask Python development questions* - diff --git a/doc/python_api/sphinx_doc_gen.py b/doc/python_api/sphinx_doc_gen.py index 241929c24d9..61dbb92938a 100644 --- a/doc/python_api/sphinx_doc_gen.py +++ b/doc/python_api/sphinx_doc_gen.py @@ -362,8 +362,6 @@ INFO_DOCS = ( "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 Add-on Tutorial: a step by step guide on how to write an add-on from scratch"), ("info_api_reference.rst", "Blender/Python API Reference Usage: examples of how to use the API reference docs"), ("info_best_practice.rst", @@ -1699,6 +1697,9 @@ def write_rst_contents(basepath): for info, info_desc in INFO_DOCS: fw(" %s <%s>\n\n" % (info_desc, info)) fw("\n") + fw("- :ref:`Blender/Python Add-on Tutorial: a step by step guide on") + fw(" how to write an add-on from scratch <blender_manual:advanced_scripting_tutorial>`\n") + fw("\n") fw(title_string("Application Modules", "=", double=True)) fw(".. toctree::\n") |