python api docs & examples for registrable Menu/Panel/Operator/PropertyGroup classes.

This commit is contained in:
Campbell Barton 2011-02-18 08:47:37 +00:00
parent c4d7bb80f5
commit 063a7f217b
18 changed files with 473 additions and 17 deletions

@ -0,0 +1,22 @@
"""
Execution Context
+++++++++++++++++
When calling an operator you may want to pass the execution context.
This determines the context thats given to the operator to run in, and weather
invoke() is called or execute().
'EXEC_DEFAULT' is used by default but you may want the operator to take user
interaction with 'INVOKE_DEFAULT'.
The execution context is as a non keyword, string argument in:
('INVOKE_DEFAULT', 'INVOKE_REGION_WIN', 'INVOKE_REGION_CHANNELS',
'INVOKE_REGION_PREVIEW', 'INVOKE_AREA', 'INVOKE_SCREEN', 'EXEC_DEFAULT',
'EXEC_REGION_WIN', 'EXEC_REGION_CHANNELS', 'EXEC_REGION_PREVIEW', 'EXEC_AREA',
'EXEC_SCREEN')
"""
# group add popup
import bpy
bpy.ops.object.group_instance_add('INVOKE_DEFAULT')

@ -0,0 +1,30 @@
"""
Calling Operators
+++++++++++++++++
Provides python access to calling operators, this includes operators written in
C, Python or Macros.
Only keyword arguments can be used to pass operator properties.
Operators don't have return values as you might expect, instead they return a
set() which is made up of: {'RUNNING_MODAL', 'CANCELLED', 'FINISHED',
'PASS_THROUGH'}.
Common return values are {'FINISHED'} and {'CANCELLED'}.
Calling an operator in the wrong context will raise a RuntimeError,
there is a poll() method to avoid this problem.
Note that the operator ID (bl_idname) in this example is 'mesh.subdivide',
'bpy.ops' is just the access path for python.
"""
import bpy
# calling an operator
bpy.ops.mesh.subdivide(number_cuts=3, smoothness=0.5)
# check poll() to avoid exception.
if bpy.ops.object.mode_set.poll():
bpy.ops.object.mode_set(mode='EDIT')

@ -0,0 +1,37 @@
"""
Submenus
++++++++
This menu demonstrates some different functions.
"""
import bpy
class SubMenu(bpy.types.Menu):
bl_idname = "OBJECT_MT_select_submenu"
bl_label = "Select"
def draw(self, context):
layout = self.layout
layout.operator("object.select_all", text="Select/Deselect All")
layout.operator("object.select_inverse", text="Inverse")
layout.operator("object.select_random", text="Random")
# access this operator as a submenu
layout.operator_menu_enum("object.select_by_type", "type", text="Select All by Type...")
layout.separator()
# expand each operator option into this menu
layout.operator_enum("object.lamp_add", "type")
layout.separator()
# use existing memu
layout.menu("VIEW3D_MT_transform")
bpy.utils.register_class(SubMenu)
# test call to display immediately.
bpy.ops.wm.call_menu(name="OBJECT_MT_select_submenu")

@ -0,0 +1,16 @@
"""
Extending Menus
+++++++++++++++
When creating menus for addons you can't reference menus in blenders default
scripts.
Instead the addon can add menu items to existing menus.
The function menu_draw acts like Menu.draw
"""
import bpy
def menu_draw(self, context):
self.layout.operator("wm.save_homefile")
bpy.types.INFO_MT_file.append(menu_draw)

@ -0,0 +1,32 @@
"""
Basic Menu Example
++++++++++++++++++
This script is a simple menu, menus differ from panels in that they must
reference from a header, panel or another menu.
Notice the 'CATEGORY_MT_name' :class:`Menu.bl_idname`, this is a naming
convention for menus.
.. note::
Menu subclasses must be registered before referencing them from blender.
"""
import bpy
class BasicMenu(bpy.types.Menu):
bl_idname = "OBJECT_MT_select_test"
bl_label = "Select"
def draw(self, context):
layout = self.layout
layout.operator("object.select_all", text="Select/Deselect All")
layout.operator("object.select_inverse", text="Inverse")
layout.operator("object.select_random", text="Random")
bpy.utils.register_class(BasicMenu)
# test call to display immediately.
bpy.ops.wm.call_menu(name="OBJECT_MT_select_test")

@ -1,6 +1,13 @@
""" """
Invoke Function Invoke Function
+++++++++++++++ +++++++++++++++
:class:`Operator.invoke` is used to initialize the operator from the context
at the moment the operator is called.
invoke() is typically used to assign properties which are then used by
execute().
Some operators don't have an execute() function, removing the ability to be
repeated from a script or macro.
This example shows how to define an operator which gets mouse input to This example shows how to define an operator which gets mouse input to
execute a function and that this operator can be invoked or executed from execute a function and that this operator can be invoked or executed from
the python api. the python api.
@ -14,7 +21,9 @@ import bpy
class SimpleMouseOperator(bpy.types.Operator): class SimpleMouseOperator(bpy.types.Operator):
"""This operator shows the mouse location, this string is used for the tooltip and API docs""" """ This operator shows the mouse location,
this string is used for the tooltip and API docs
"""
bl_idname = "wm.mouse_position" bl_idname = "wm.mouse_position"
bl_label = "Invoke Mouse Operator" bl_label = "Invoke Mouse Operator"

@ -1,7 +1,17 @@
""" """
Calling a File Selector Calling a File Selector
+++++++++++++++++++++++ +++++++++++++++++++++++
This example shows how an operator can use the file selector This example shows how an operator can use the file selector.
Notice the invoke function calls a window manager method and returns
RUNNING_MODAL, this means the file selector stays open and the operator does not
exit immediately after invoke finishes.
The file selector runs the operator, calling :class:`Operator.execute` when the
user confirms.
The :class:`Operator.poll` function is optional, used to check if the operator
can run.
""" """
import bpy import bpy
@ -13,9 +23,13 @@ class ExportSomeData(bpy.types.Operator):
filepath = bpy.props.StringProperty(subtype="FILE_PATH") filepath = bpy.props.StringProperty(subtype="FILE_PATH")
@classmethod
def poll(cls, context):
return context.object is not None
def execute(self, context): def execute(self, context):
file = open(self.filepath, 'w') file = open(self.filepath, 'w')
file.write("Hello World") file.write("Hello World " + context.object.name)
return {'FINISHED'} return {'FINISHED'}
def invoke(self, context, event): def invoke(self, context, event):

@ -0,0 +1,31 @@
"""
Dialog Box
++++++++++
This operator uses its :class:`Operator.invoke` function to call a popup.
"""
import bpy
class DialogOperator(bpy.types.Operator):
bl_idname = "object.modal_operator"
bl_label = "Simple Modal Operator"
my_float = bpy.props.FloatProperty(name="Some Floating Point")
my_bool = bpy.props.BoolProperty(name="Toggle Option")
my_string = bpy.props.StringProperty(name="String Value")
def execute(self, context):
message = "Popup Values: %f, %d, '%s'" % \
(self.my_float, self.my_bool, self.my_string)
self.report({'INFO'}, message)
return {'FINISHED'}
def invoke(self, context, event):
wm = context.window_manager
return wm.invoke_props_dialog(self)
bpy.utils.register_class(DialogOperator)
# test call
bpy.ops.object.modal_operator('INVOKE_DEFAULT')

@ -0,0 +1,46 @@
"""
Custom Drawing
++++++++++++++
By default operator properties use an automatic user interface layout.
If you need more control you can create your own layout with a
:class:`Operator.draw` function.
This works like the :class:`Panel` and :class:`Menu` draw functions, its used
for dialogs and file selectors.
"""
import bpy
class CustomDrawOperator(bpy.types.Operator):
bl_idname = "object.custom_draw"
bl_label = "Simple Modal Operator"
filepath = bpy.props.StringProperty(subtype="FILE_PATH")
my_float = bpy.props.FloatProperty(name="Float")
my_bool = bpy.props.BoolProperty(name="Toggle Option")
my_string = bpy.props.StringProperty(name="String Value")
def execute(self, context):
print()
return {'FINISHED'}
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
return {'RUNNING_MODAL'}
def draw(self, context):
layout = self.layout
col = layout.column()
col.label(text="Custom Interface!")
row = col.row()
row.prop(self, "my_float")
row.prop(self, "my_bool")
col.prop(self, "my_string")
bpy.utils.register_class(CustomDrawOperator)
# test call
bpy.ops.object.custom_draw('INVOKE_DEFAULT')

@ -0,0 +1,58 @@
"""
Modal Execution
+++++++++++++++
This operator defines a :class:`Operator.modal` function which running,
handling events until it returns {'FINISHED'} or {'CANCELLED'}.
Grab, Rotate, Scale and Fly-Mode are examples of modal operators.
They are especially useful for interactive tools,
your operator can have its own state where keys toggle options as the operator
runs.
:class:`Operator.invoke` is used to initialize the operator as being by
returning {'RUNNING_MODAL'}, initializing the modal loop.
Notice __init__() and __del__() are declared.
For other operator types they are not useful but for modal operators they will
be called before the :class:`Operator.invoke` and after the operator finishes.
"""
import bpy
class ModalOperator(bpy.types.Operator):
bl_idname = "object.modal_operator"
bl_label = "Simple Modal Operator"
def __init__(self):
print("Start")
def __del__(self):
print("End")
def execute(self, context):
context.object.location.x = self.value / 100.0
def modal(self, context, event):
if event.type == 'MOUSEMOVE': # Apply
self.value = event.mouse_x
self.execute(context)
elif event.type == 'LEFTMOUSE': # Confirm
return {'FINISHED'}
elif event.type in ('RIGHTMOUSE', 'ESC'): # Cancel
return {'CANCELLED'}
return {'RUNNING_MODAL'}
def invoke(self, context, event):
self.value = event.mouse_x
self.execute(context)
print(context.window_manager.modal_handler_add(self))
return {'RUNNING_MODAL'}
bpy.utils.register_class(ModalOperator)
# test call
bpy.ops.object.modal_operator('INVOKE_DEFAULT')

@ -1,7 +1,14 @@
""" """
Basic Operator Example Basic Operator Example
++++++++++++++++++++++ ++++++++++++++++++++++
This script is the most simple operator you can write that does something. This script shows simple operator which prints a message.
Since the operator only has an :class:`Operator.execute` function it takes no
user input.
.. note::
Operator subclasses must be registered before accessing them from blender.
""" """
import bpy import bpy

@ -0,0 +1,44 @@
"""
Simple Object Panel
+++++++++++++++++++
This panel has a :class:`Panel.poll` and :class:`Panel.draw_header` function,
even though the contents is basic this closely resemples blenders panels.
"""
import bpy
class ObjectSelectPanel(bpy.types.Panel):
bl_idname = "OBJECT_PT_select"
bl_label = "Select"
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "object"
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
return (context.object is not None)
def draw_header(self, context):
layout = self.layout
obj = context.object
layout.prop(obj, "select", text="")
def draw(self, context):
layout = self.layout
obj = context.object
row = layout.row()
row.prop(obj, "hide_select")
row.prop(obj, "hide_render")
box = layout.box()
box.label("Selection Tools")
box.operator("object.select_all")
row = box.row()
row.operator("object.select_inverse")
row.operator("object.select_random")
bpy.utils.register_class(ObjectSelectPanel)

@ -0,0 +1,35 @@
"""
Mix-in Classes
++++++++++++++
A mix-in parent class can be used to share common properties and
:class:`Menu.poll` function.
"""
import bpy
class View3DPanel():
bl_space_type = 'VIEW_3D'
bl_region_type = 'TOOLS'
@classmethod
def poll(cls, context):
return (context.object is not None)
class PanelOne(View3DPanel, bpy.types.Panel):
bl_idname = "VIEW3D_PT_test_1"
bl_label = "Panel One"
def draw(self, context):
self.layout.label("Small Class")
class PanelTwo(View3DPanel, bpy.types.Panel):
bl_idname = "VIEW3D_PT_test_2"
bl_label = "Panel Two"
def draw(self, context):
self.layout.label("Also Small Class")
bpy.utils.register_class(PanelOne)
bpy.utils.register_class(PanelTwo)

@ -0,0 +1,28 @@
"""
Basic Panel Example
+++++++++++++++++++
This script is a simple panel which will draw into the object properties
section.
Notice the 'CATEGORY_PT_name' :class:`Panel.bl_idname`, this is a naming
convention for panels.
.. note::
Panel subclasses must be registered for blender to use them.
"""
import bpy
class HelloWorldPanel(bpy.types.Panel):
bl_idname = "OBJECT_PT_hello_world"
bl_label = "Hello World"
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "object"
def draw(self, context):
self.layout.label(text="Hello World")
bpy.utils.register_class(HelloWorldPanel)

@ -0,0 +1,40 @@
"""
Custom Properties
+++++++++++++++++
PropertyGroups are the base class for dynamically defined sets of properties.
They can be used to extend existing blender data with your own types which can
be animated, accessed from the user interface and from python.
.. note::
The values assigned to blender data are saved to disk but the class
definitions are not, this means whenever you load blender the class needs
to be registered too.
This is best done by creating an addon which loads on startup and registers
your properties.
.. note::
PropertyGroups must be registered before assigning them to blender data.
.. seealso::
Property types used in class declarations are all in :mod:`bpy.props`
"""
import bpy
class MyPropertyGroup(bpy.types.PropertyGroup):
custom_1 = bpy.props.FloatProperty(name="My Float")
custom_2 = bpy.props.IntProperty(name="My Int")
bpy.utils.register_class(MyPropertyGroup)
bpy.types.Object.my_properties = MyPropertyGroup
# test this worked
bpy.data.objects[0].my_properties.custom_1 = 22.0

@ -61,9 +61,10 @@ else:
"bpy.app", "bpy.app",
"bpy.path", "bpy.path",
"bpy.data", "bpy.data",
"bpy.props", # "bpy.props",
"bpy.utils", "bpy.utils",
#"bpy.types", # supports filtering "bpy.context",
# "bpy.types", # supports filtering
"bpy.ops", # supports filtering "bpy.ops", # supports filtering
"bge", "bge",
"aud", "aud",
@ -73,8 +74,8 @@ else:
"mathutils.geometry", "mathutils.geometry",
) )
FILTER_BPY_TYPES = ("Operator", ) # allow FILTER_BPY_TYPES = ("PropertyGroup", "Panel", "Menu", "Operator") # allow
FILTER_BPY_OPS = ("import_scene", ) # allow FILTER_BPY_OPS = ("import.scene", ) # allow
# for quick rebuilds # for quick rebuilds
""" """
@ -156,19 +157,20 @@ def example_extract_docstring(filepath):
def write_example_ref(ident, fw, example_id, ext="py"): def write_example_ref(ident, fw, example_id, ext="py"):
if example_id in EXAMPLE_SET: if example_id in EXAMPLE_SET:
# extract the comment # extract the comment
filepath = "../examples/%s.%s" % (example_id, ext) filepath = "../examples/%s.%s" % (example_id, ext)
filepath_full = os.path.join(os.path.dirname(fw.__self__.name), filepath) filepath_full = os.path.join(os.path.dirname(fw.__self__.name), filepath)
text, line_no = example_extract_docstring(filepath_full) text, line_no = example_extract_docstring(filepath_full)
for line in text.split("\n"): for line in text.split("\n"):
fw("%s\n" % (ident + line).rstrip()) fw("%s\n" % (ident + line).rstrip())
fw("\n") fw("\n")
fw("%s.. literalinclude:: %s\n" % (ident, filepath)) fw("%s.. literalinclude:: %s\n" % (ident, filepath))
fw("%s :lines: %d-\n" % (ident, line_no)) if line_no > 0:
fw("%s :lines: %d-\n" % (ident, line_no))
fw("\n") fw("\n")
EXAMPLE_SET_USED.add(example_id) EXAMPLE_SET_USED.add(example_id)
else: else:
@ -563,6 +565,7 @@ def pyrna2sphinx(BASEPATH):
fw(".. module:: bpy.types\n\n") fw(".. module:: bpy.types\n\n")
# docs first?, ok
write_example_ref("", fw, "bpy.types.%s" % struct.identifier) write_example_ref("", fw, "bpy.types.%s" % struct.identifier)
base_ids = [base.identifier for base in struct.get_bases()] base_ids = [base.identifier for base in struct.get_bases()]
@ -726,6 +729,9 @@ def pyrna2sphinx(BASEPATH):
fw(" * :class:`%s`\n" % ref) fw(" * :class:`%s`\n" % ref)
fw("\n") fw("\n")
# docs last?, disable for now
# write_example_ref("", fw, "bpy.types.%s" % struct.identifier)
if "bpy.types" not in EXCLUDE_MODULES: if "bpy.types" not in EXCLUDE_MODULES:
for struct in structs.values(): for struct in structs.values():
# TODO, rna_info should filter these out! # TODO, rna_info should filter these out!
@ -854,9 +860,10 @@ def rna2sphinx(BASEPATH):
fw("\n") fw("\n")
fw("This document is an API reference for Blender %s. built %s.\n" % (version_string, bpy.app.build_date)) fw("This document is an API reference for Blender %s. built %s.\n" % (version_string, bpy.app.build_date))
fw("\n") fw("\n")
fw("An introduction to Blender and Python can be found at <http://wiki.blender.org/index.php/Dev:2.5/Py/API/Intro>\n") fw("| An introduction to Blender and Python can be found at `Quickstart Intro <http://wiki.blender.org/index.php/Dev:2.5/Py/API/Intro>`_,\n")
fw("| For a more general explanation of blender/python see the `API Overview <http://wiki.blender.org/index.php/Dev:2.5/Py/API/Overview>`_\n")
fw("\n") fw("\n")
fw("`A PDF version of this document is also available <blender_python_reference_%s.pdf>`__\n" % version_string_fp) fw("`A PDF version of this document is also available <blender_python_reference_%s.pdf>`_\n" % version_string_fp)
fw("\n") fw("\n")
fw(".. warning:: The Python API in Blender is **UNSTABLE**, It should only be used for testing, any script written now may break in future releases.\n") fw(".. warning:: The Python API in Blender is **UNSTABLE**, It should only be used for testing, any script written now may break in future releases.\n")
fw(" \n") fw(" \n")
@ -943,6 +950,7 @@ def rna2sphinx(BASEPATH):
fw = file.write fw = file.write
fw("Operators (bpy.ops)\n") fw("Operators (bpy.ops)\n")
fw("===================\n\n") fw("===================\n\n")
write_example_ref("", fw, "bpy.ops")
fw(".. toctree::\n") fw(".. toctree::\n")
fw(" :glob:\n\n") fw(" :glob:\n\n")
fw(" bpy.ops.*\n\n") fw(" bpy.ops.*\n\n")
@ -1010,7 +1018,6 @@ def rna2sphinx(BASEPATH):
import mathutils as module import mathutils as module
pymodule2sphinx(BASEPATH, "mathutils", module, "Math Types & Utilities (mathutils)") pymodule2sphinx(BASEPATH, "mathutils", module, "Math Types & Utilities (mathutils)")
if "mathutils.geometry" not in EXCLUDE_MODULES: if "mathutils.geometry" not in EXCLUDE_MODULES:
import mathutils.geometry as module import mathutils.geometry as module
pymodule2sphinx(BASEPATH, "mathutils.geometry", module, "Geometry Utilities (mathutils.geometry)") pymodule2sphinx(BASEPATH, "mathutils.geometry", module, "Geometry Utilities (mathutils.geometry)")

@ -674,7 +674,7 @@ class _GenericUI:
@classmethod @classmethod
def append(cls, draw_func): def append(cls, draw_func):
"""Prepend an draw function to this menu, takes the same arguments as the menus draw function.""" """Append a draw function to this menu, takes the same arguments as the menus draw function."""
draw_funcs = cls._dyn_ui_initialize() draw_funcs = cls._dyn_ui_initialize()
draw_funcs.append(draw_func) draw_funcs.append(draw_func)

@ -348,7 +348,7 @@ class MATERIAL_PT_shading(MaterialButtonsPanel, bpy.types.Panel):
class MATERIAL_PT_transp(MaterialButtonsPanel, bpy.types.Panel): class MATERIAL_PT_transp(MaterialButtonsPanel, bpy.types.Panel):
bl_label = "Transparency" bl_label = "Transparency"
bl_options = {'DEFAULT_CLOSED'} # bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {'BLENDER_RENDER'} COMPAT_ENGINES = {'BLENDER_RENDER'}
@classmethod @classmethod