Merge branch 'master' into blender2.8
This commit is contained in:
@@ -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*
|
||||
|
||||
@@ -341,6 +341,8 @@ EXTRA_SOURCE_FILES = (
|
||||
"../examples/bge.texture.py",
|
||||
"../examples/bmesh.ops.1.py",
|
||||
"../examples/bpy.app.translations.py",
|
||||
"../static/favicon.ico",
|
||||
"../static/blender_logo.svg",
|
||||
)
|
||||
|
||||
|
||||
@@ -362,8 +364,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",
|
||||
@@ -1648,14 +1648,14 @@ def write_sphinx_conf_py(basepath):
|
||||
|
||||
if ARGS.sphinx_theme == "blender-org":
|
||||
fw("html_theme_path = ['../']\n")
|
||||
# copied with the theme, exclude else we get an error [T28873]
|
||||
fw("html_favicon = 'favicon.ico'\n") # in <theme>/static/
|
||||
|
||||
# not helpful since the source is generated, adds to upload size.
|
||||
fw("html_copy_source = False\n")
|
||||
fw("html_show_sphinx = False\n")
|
||||
fw("html_split_index = True\n")
|
||||
fw("\n")
|
||||
fw("html_extra_path = ['__/static/favicon.ico', '__/static/blender_logo.svg']\n")
|
||||
fw("html_favicon = '__/static/favicon.ico'\n")
|
||||
fw("html_logo = '__/static/blender_logo.svg'\n\n")
|
||||
|
||||
# needed for latex, pdf gen
|
||||
fw("latex_elements = {\n")
|
||||
@@ -1701,6 +1701,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_addon_tutorial>`\n")
|
||||
fw("\n")
|
||||
|
||||
fw(title_string("Application Modules", "=", double=True))
|
||||
fw(".. toctree::\n")
|
||||
|
||||
116
doc/python_api/static/blender_logo.svg
Normal file
116
doc/python_api/static/blender_logo.svg
Normal file
@@ -0,0 +1,116 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
id="svg8"
|
||||
version="1.1"
|
||||
viewBox="0 0 55.032989 15.935012"
|
||||
height="60.226818"
|
||||
width="207.9987">
|
||||
<defs
|
||||
id="defs2">
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath3020">
|
||||
<path
|
||||
style="stroke-width:1.06666672"
|
||||
d="M 0,0 H 211.2 V 61.866667 H 0 Z"
|
||||
id="path3022" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath3020-8">
|
||||
<path
|
||||
style="stroke-width:1.06666672"
|
||||
d="M 0,0 H 211.2 V 61.866667 H 0 Z"
|
||||
id="path3022-1" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>Blender Logo</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:source>https://www.blender.org/about/logo/</dc:source>
|
||||
<cc:license
|
||||
rdf:resource="(c) Blender Foundation" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
transform="translate(42.023693,-77.734934)"
|
||||
id="layer1">
|
||||
<g
|
||||
transform="matrix(0.26259939,0,0,-0.26259939,-42.237694,93.888967)"
|
||||
id="g3012"
|
||||
style="stroke-width:7.2904439">
|
||||
<g
|
||||
id="g3014"
|
||||
style="stroke-width:7.2904439" />
|
||||
<g
|
||||
id="g3016"
|
||||
style="stroke-width:7.2904439">
|
||||
<g
|
||||
clip-path="url(#clipPath3020-8)"
|
||||
id="g3018"
|
||||
style="stroke-width:7.2904439">
|
||||
<path
|
||||
id="path3024"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:7.77647352"
|
||||
d="m 192.54827,44.510933 c 0,0 -125.158403,0 -128.688003,0 -0.07893,0.06187 -0.1504,0.1344 -0.2272,0.193067 -0.01813,0.0192 -18.9312,14.548267 -19.5008,14.986667 -0.032,0.0256 -0.06507,0.04907 -0.06507,0.04907 -3.029333,2.321067 -7.531733,2.369067 -10.721067,0.133334 -1.9968,-1.396267 -3.194666,-3.508267 -3.285333,-5.792 -0.0021,-0.09173 -0.0064,-0.1824 -0.0064,-0.2752 0,-1.185067 0.314667,-2.3296 0.8832,-3.357867 -5.857067,-0.0053 -11.746133,-0.0128 -11.746133,-0.0128 -4.3936,-0.0021 -8.3648,-2.965333 -9.2725337,-6.8928 C 9.7898667,42.9792 9.7248,42.411733 9.7248,41.8496 c 0,-1.643733 0.546133,-3.236267 1.5808,-4.542933 1.162667,-1.463467 2.842667,-2.448 4.7584,-2.832 C 10.530133,30.2272 5.0016,25.984 4.9973333,25.979733 4.9578667,25.949867 4.9248,25.924267 4.8970667,25.905067 c -2.2176,-1.703467 -3.68,-4.1728 -4.0106667,-6.770134 -0.0490667,-0.381866 -0.0714667,-0.7584 -0.0714667,-1.133866 0,-1.870934 0.5962667,-3.650134 1.7301334,-5.102934 1.3781333,-1.764266 3.4144,-2.884266 5.7322666,-3.147733 2.6549337,-0.3072 5.4495997,0.542933 7.6607997,2.330667 0.01493,0.01173 2.753067,2.256 5.639467,4.6176 1.060267,-2.555734 2.545067,-4.926934 4.465067,-7.0453337 2.4704,-2.7306666 5.473066,-4.8864 8.919466,-6.4128 C 38.5856,1.6352 42.466133,0.82453333 46.501333,0.83413333 50.5376,0.8416 54.417067,1.6629333 58.0352,3.2768 c 3.453867,1.5466667 6.449067,3.7109333 8.910933,6.4352 0.360534,0.4032 0.693334,0.8288 1.0272,1.2544 4.885334,0 124.574937,0 124.574937,0 9.84,0 17.8368,7.5232 17.8368,16.7712 0,9.248 -7.9968,16.773333 -17.8368,16.773333" />
|
||||
<path
|
||||
id="path3026"
|
||||
style="fill:#0d528a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:7.77647352"
|
||||
d="m 38.538667,28.497067 c 0.113066,2.016 1.101866,3.793066 2.590933,5.0528 1.4624,1.237333 3.428267,1.9936 5.575467,1.9936 2.144,0 4.110933,-0.756267 5.572266,-1.9936 1.490134,-1.259734 2.4768,-3.0368 2.590934,-5.050667 0.113066,-2.0736 -0.718934,-3.997867 -2.181334,-5.425067 C 51.1968,21.6224 49.0784,20.7104 46.705067,20.7104 c -2.3744,0 -4.497067,0.912 -5.9872,2.363733 -1.461334,1.4272 -2.292267,3.351467 -2.1792,5.422934" />
|
||||
<path
|
||||
id="path3028"
|
||||
style="fill:#f5792a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:7.77647352"
|
||||
d="m 25.536,24.421333 c 0.01387,-0.7904 0.2656,-2.3232 0.6432,-3.521066 C 26.9728,18.3648 28.318933,16.0192 30.193067,13.950933 32.1152,11.8272 34.482133,10.119467 37.216,8.9088 c 2.872533,-1.2736 5.986133,-1.9210667 9.220267,-1.9157333 3.229866,0.00427 6.3424,0.6656 9.216,1.9477333 2.733866,1.2224 5.098666,2.9376 7.018666,5.063467 1.870934,2.074666 3.216,4.424533 4.010667,6.96 0.401067,1.282133 0.6528,2.581333 0.754133,3.886933 0.100267,1.285333 0.0576,2.571733 -0.1248,3.858133 -0.356266,2.507734 -1.223466,4.858667 -2.557866,7.002667 -1.2224,1.970133 -2.7968,3.696 -4.6688,5.147733 l 0.0053,0.0021 -18.8928,14.506666 c -0.016,0.0128 -0.02987,0.02667 -0.048,0.03733 -1.240533,0.952533 -3.3248,0.948266 -4.686933,-0.0053 -1.3792,-0.9632 -1.536,-2.557866 -0.3104,-3.5648 l -0.0043,-0.0043 7.8784,-6.407467 -24.016,-0.02667 c -0.01173,0 -0.0224,0 -0.032,0 -1.985067,-0.0011 -3.893333,-1.303466 -4.269867,-2.9504 -0.389333,-1.6768 0.958934,-3.067733 3.022934,-3.074133 l -0.0021,-0.0075 L 30.900267,39.3888 9.1786667,22.715733 c -0.026667,-0.02133 -0.0576,-0.0416 -0.0832,-0.06293 -2.0490667,-1.568 -2.7104,-4.178133 -1.4197334,-5.828267 1.3088,-1.68 4.0949337,-1.6832 6.1653337,-0.0096 L 25.696,26.516267 c 0,0 -0.173867,-1.3088 -0.16,-2.094934 z M 56,20.034133 c -2.443733,-2.488533 -5.8624,-3.899733 -9.563733,-3.9072 -3.7056,-0.0064 -7.124267,1.393067 -9.568,3.877334 -1.1936,1.210666 -2.0704,2.602666 -2.6112,4.087466 -0.529067,1.457067 -0.736,3.0048 -0.599467,4.5664 0.130133,1.527467 0.583467,2.9824 1.309867,4.3008 0.712533,1.293867 1.6928,2.465067 2.9056,3.454934 2.373333,1.934933 5.396266,2.981333 8.558933,2.9856 3.1648,0.0053 6.1856,-1.0336 8.561067,-2.961067 1.2096,-0.9856 2.190933,-2.151467 2.903466,-3.445333 0.728534,-1.316267 1.179734,-2.765867 1.314134,-4.2976 C 59.344,27.136 59.138133,25.5904 58.609067,24.1312 58.0672,22.6432 57.1936,21.2512 56,20.034133" />
|
||||
<path
|
||||
id="path3030"
|
||||
style="fill:#0d528a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:7.77647352"
|
||||
d="m 167.96587,28.529067 c 0.1824,2.0352 1.75466,3.325866 4.15786,3.325866 2.4064,0 3.97974,-1.290666 4.16214,-3.325866 z M 176.0384,23.504 c -0.61547,-1.508267 -2.06507,-2.401067 -4.03627,-2.401067 -2.46613,0 -4.12693,1.540267 -4.1952,3.9744 h 14.18347 c 0,0.251734 0,0.462934 0,0.715734 0,6.094933 -3.57653,9.518933 -9.98827,9.518933 -6.22506,0 -9.98826,-3.457067 -9.98826,-8.878933 0,-5.454934 3.82293,-8.9056 9.98826,-8.9056 3.70134,0 6.5696,1.2672 8.33814,3.4976 L 176.0384,23.504" />
|
||||
<path
|
||||
id="path3032"
|
||||
style="fill:#0d528a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:7.77647352"
|
||||
d="m 105.0336,28.519467 c 0.18667,2.036266 1.76,3.326933 4.16533,3.326933 2.40214,0 3.97547,-1.290667 4.15894,-3.326933 z m 8.07787,-5.025067 c -0.61547,-1.512533 -2.06614,-2.404267 -4.03947,-2.404267 -2.464,0 -4.12907,1.544534 -4.19413,3.9776 h 14.18026 c 0,0.2496 0,0.4608 0,0.712534 0,6.098133 -3.57546,9.524266 -9.98613,9.524266 -6.22933,0 -9.986133,-3.458133 -9.986133,-8.88 0,-5.456 3.821863,-8.906666 9.986133,-8.906666 3.6992,0 6.5696,1.262933 8.34133,3.495466 l -4.30186,2.481067" />
|
||||
<path
|
||||
id="path3034"
|
||||
style="fill:#0d528a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:7.77647352"
|
||||
d="m 92.701867,38.997333 h 5.114666 v -20.9248 h -5.114666 z" />
|
||||
<path
|
||||
id="path3036"
|
||||
style="fill:#0d528a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:7.77647352"
|
||||
d="m 120.33067,33.438933 h 5.14453 v -1.245866 c 1.8208,1.9424 4.00747,2.9248 6.47253,2.9248 2.84054,0 4.992,-0.9824 6.1024,-2.653867 0.92587,-1.381333 0.98774,-3.0496 0.98774,-5.239467 v -9.152 h -5.15094 v 8.040534 c 0,3.336533 -0.67093,4.877866 -3.6,4.877866 -2.96106,0 -4.81173,-1.764266 -4.81173,-4.724266 v -8.194134 h -5.14453 v 15.3664" />
|
||||
<path
|
||||
id="path3038"
|
||||
style="fill:#0d528a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:7.77647352"
|
||||
d="m 155.60107,26.173867 c 0,-2.864 -1.9136,-4.741334 -4.8736,-4.741334 -2.96427,0 -4.87787,1.813334 -4.87787,4.709334 0,2.9376 1.88907,4.750933 4.87787,4.750933 2.96,0 4.8736,-1.8464 4.8736,-4.718933 z m 0,6.568533 c -1.30027,1.393067 -3.1808,2.157867 -5.79734,2.157867 -5.64266,0 -9.49546,-3.479467 -9.49546,-8.6944 0,-5.112534 3.82613,-8.688 9.40266,-8.688 2.5568,0 4.4352,0.645333 5.89014,2.096 v -1.541334 h 5.14666 v 22.2528 l -5.14666,-1.328 V 32.7424" />
|
||||
<path
|
||||
id="path3040"
|
||||
style="fill:#0d528a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:7.77647352"
|
||||
d="m 81.010133,30.8928 c 2.990934,0 4.871467,-1.813333 4.871467,-4.750933 0,-2.896 -1.9104,-4.709334 -4.871467,-4.709334 -2.958933,0 -4.869333,1.877334 -4.869333,4.741334 0,2.872533 1.9104,4.718933 4.869333,4.718933 z m -4.869333,8.104533 -5.1488,1.328 v -22.2528 h 5.1488 v 1.541334 c 1.448533,-1.450667 3.329067,-2.096 5.886933,-2.096 5.579734,0 9.4016,3.575466 9.4016,8.688 0,5.214933 -3.853866,8.6944 -9.493333,8.6944 -2.621867,0 -4.5024,-0.7648 -5.7952,-2.157867 v 6.254933" />
|
||||
<path
|
||||
id="path3042"
|
||||
style="fill:#0d528a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:7.77647352"
|
||||
d="m 183.2608,18.072533 v 15.3664 h 5.14667 v -0.9504 c 1.54026,1.857067 3.1712,2.7808 5.0272,2.7808 0.368,0 0.82986,-0.05973 1.44533,-0.1216 V 30.768 c -0.496,0.064 -1.04747,0.064 -1.6352,0.064 -2.992,0 -4.83733,-1.972267 -4.83733,-5.329067 v -7.4304 h -5.14667" />
|
||||
<path
|
||||
id="path3044"
|
||||
style="fill:#0d528a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:7.77647352"
|
||||
d="m 201.55307,35.252267 h -0.39467 l 0.0139,1.409066 0.0203,0.466134 -0.1056,-0.384 -0.46826,-1.4912 h -0.36054 l -0.45866,1.4912 -0.1152,0.376533 0.0277,-0.458667 0.0139,-1.409066 h -0.38614 v 2.282666 h 0.54187 l 0.57067,-1.8016 0.5568,1.8016 h 0.544 z m -3.12747,0 h -0.39787 V 37.1968 h -0.7328 v 0.338133 h 1.85067 V 37.1968 h -0.72 v -1.944533" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Reference in New Issue
Block a user