python api docs & examples for registrable Menu/Panel/Operator/PropertyGroup classes.
authorCampbell Barton <ideasman42@gmail.com>
Fri, 18 Feb 2011 08:47:37 +0000 (08:47 +0000)
committerCampbell Barton <ideasman42@gmail.com>
Fri, 18 Feb 2011 08:47:37 +0000 (08:47 +0000)
18 files changed:
doc/python_api/examples/bpy.ops.1.py [new file with mode: 0644]
doc/python_api/examples/bpy.ops.py [new file with mode: 0644]
doc/python_api/examples/bpy.types.Menu.1.py [new file with mode: 0644]
doc/python_api/examples/bpy.types.Menu.2.py [new file with mode: 0644]
doc/python_api/examples/bpy.types.Menu.py [new file with mode: 0644]
doc/python_api/examples/bpy.types.Operator.1.py
doc/python_api/examples/bpy.types.Operator.2.py
doc/python_api/examples/bpy.types.Operator.3.py [new file with mode: 0644]
doc/python_api/examples/bpy.types.Operator.4.py [new file with mode: 0644]
doc/python_api/examples/bpy.types.Operator.5.py [new file with mode: 0644]
doc/python_api/examples/bpy.types.Operator.py
doc/python_api/examples/bpy.types.Panel.1.py [new file with mode: 0644]
doc/python_api/examples/bpy.types.Panel.2.py [new file with mode: 0644]
doc/python_api/examples/bpy.types.Panel.py [new file with mode: 0644]
doc/python_api/examples/bpy.types.PropertyGroup.py [new file with mode: 0644]
doc/python_api/sphinx_doc_gen.py
release/scripts/modules/bpy_types.py
release/scripts/ui/properties_material.py

diff --git a/doc/python_api/examples/bpy.ops.1.py b/doc/python_api/examples/bpy.ops.1.py
new file mode 100644 (file)
index 0000000..f43be2b
--- /dev/null
@@ -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')
diff --git a/doc/python_api/examples/bpy.ops.py b/doc/python_api/examples/bpy.ops.py
new file mode 100644 (file)
index 0000000..487d603
--- /dev/null
@@ -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')
diff --git a/doc/python_api/examples/bpy.types.Menu.1.py b/doc/python_api/examples/bpy.types.Menu.1.py
new file mode 100644 (file)
index 0000000..fa23e2d
--- /dev/null
@@ -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")
diff --git a/doc/python_api/examples/bpy.types.Menu.2.py b/doc/python_api/examples/bpy.types.Menu.2.py
new file mode 100644 (file)
index 0000000..4579dd6
--- /dev/null
@@ -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)
diff --git a/doc/python_api/examples/bpy.types.Menu.py b/doc/python_api/examples/bpy.types.Menu.py
new file mode 100644 (file)
index 0000000..a6abba6
--- /dev/null
@@ -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")
index 03a3f6d70d6203b42334c1358f625c2cdbb7fba7..133dc09af46999bd83da5d3fe0454233bd8490c0 100644 (file)
@@ -1,6 +1,13 @@
 """
 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
 execute a function and that this operator can be invoked or executed from
 the python api.
@@ -14,7 +21,9 @@ import bpy
 
 
 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_label = "Invoke Mouse Operator"
 
index 1673b22fbf1e33f78471b97e6f586f128f1e1602..54bd481b33938438de13e3879d94ea8b3526ce1d 100644 (file)
@@ -1,7 +1,17 @@
 """
 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
 
@@ -13,9 +23,13 @@ class ExportSomeData(bpy.types.Operator):
 
     filepath = bpy.props.StringProperty(subtype="FILE_PATH")
 
+    @classmethod
+    def poll(cls, context):
+        return context.object is not None
+
     def execute(self, context):
         file = open(self.filepath, 'w')
-        file.write("Hello World")
+        file.write("Hello World " + context.object.name)
         return {'FINISHED'}
 
     def invoke(self, context, event):
diff --git a/doc/python_api/examples/bpy.types.Operator.3.py b/doc/python_api/examples/bpy.types.Operator.3.py
new file mode 100644 (file)
index 0000000..887657b
--- /dev/null
@@ -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')
diff --git a/doc/python_api/examples/bpy.types.Operator.4.py b/doc/python_api/examples/bpy.types.Operator.4.py
new file mode 100644 (file)
index 0000000..4cb7b02
--- /dev/null
@@ -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')
diff --git a/doc/python_api/examples/bpy.types.Operator.5.py b/doc/python_api/examples/bpy.types.Operator.5.py
new file mode 100644 (file)
index 0000000..fd09120
--- /dev/null
@@ -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')
index 2efa99ce795486eba645d6879b6cbb01d9556159..52edfa0a61b93a59e906a0a40170e4a433d1d191 100644 (file)
@@ -1,7 +1,14 @@
 """
 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
 
diff --git a/doc/python_api/examples/bpy.types.Panel.1.py b/doc/python_api/examples/bpy.types.Panel.1.py
new file mode 100644 (file)
index 0000000..2fe476c
--- /dev/null
@@ -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)
\ No newline at end of file
diff --git a/doc/python_api/examples/bpy.types.Panel.2.py b/doc/python_api/examples/bpy.types.Panel.2.py
new file mode 100644 (file)
index 0000000..661d7cb
--- /dev/null
@@ -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)
diff --git a/doc/python_api/examples/bpy.types.Panel.py b/doc/python_api/examples/bpy.types.Panel.py
new file mode 100644 (file)
index 0000000..210665b
--- /dev/null
@@ -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)
\ No newline at end of file
diff --git a/doc/python_api/examples/bpy.types.PropertyGroup.py b/doc/python_api/examples/bpy.types.PropertyGroup.py
new file mode 100644 (file)
index 0000000..219cd28
--- /dev/null
@@ -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
index 38218675484ce699fb37ac1d8d0f7dae5746a779..5f9505a7ea30653b51812c85d2e0b6032c3fc24f 100644 (file)
@@ -61,9 +61,10 @@ else:
         "bpy.app",
         "bpy.path",
         "bpy.data",
-        "bpy.props",
+        "bpy.props",
         "bpy.utils",
-        #"bpy.types",  # supports filtering
+        "bpy.context",
+        # "bpy.types",  # supports filtering
         "bpy.ops",  # supports filtering
         "bge",
         "aud",
@@ -73,8 +74,8 @@ else:
         "mathutils.geometry",
     )
 
-    FILTER_BPY_TYPES = ("Operator", )  # allow 
-    FILTER_BPY_OPS = ("import_scene", )  # allow 
+    FILTER_BPY_TYPES = ("PropertyGroup", "Panel", "Menu", "Operator")  # allow
+    FILTER_BPY_OPS = ("import.scene", )  # allow
 
     # for quick rebuilds
     """
@@ -156,19 +157,20 @@ def example_extract_docstring(filepath):
 
 def write_example_ref(ident, fw, example_id, ext="py"):
     if example_id in EXAMPLE_SET:
-        
+
         # extract the comment
         filepath = "../examples/%s.%s" % (example_id, ext)
         filepath_full = os.path.join(os.path.dirname(fw.__self__.name), filepath)
-        
+
         text, line_no = example_extract_docstring(filepath_full)
-        
+
         for line in text.split("\n"):
             fw("%s\n" % (ident + line).rstrip())
         fw("\n")
 
         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")
         EXAMPLE_SET_USED.add(example_id)
     else:
@@ -563,6 +565,7 @@ def pyrna2sphinx(BASEPATH):
 
         fw(".. module:: bpy.types\n\n")
 
+        # docs first?, ok
         write_example_ref("", fw, "bpy.types.%s" % struct.identifier)
 
         base_ids = [base.identifier for base in struct.get_bases()]
@@ -726,6 +729,9 @@ def pyrna2sphinx(BASEPATH):
                 fw("   * :class:`%s`\n" % ref)
             fw("\n")
 
+        # docs last?, disable for now
+        # write_example_ref("", fw, "bpy.types.%s" % struct.identifier)
+
     if "bpy.types" not in EXCLUDE_MODULES:
         for struct in structs.values():
             # TODO, rna_info should filter these out!
@@ -854,9 +860,10 @@ def rna2sphinx(BASEPATH):
     fw("\n")
     fw("This document is an API reference for Blender %s. built %s.\n" % (version_string, bpy.app.build_date))
     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("`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(".. 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")
@@ -943,6 +950,7 @@ def rna2sphinx(BASEPATH):
         fw = file.write
         fw("Operators (bpy.ops)\n")
         fw("===================\n\n")
+        write_example_ref("", fw, "bpy.ops")
         fw(".. toctree::\n")
         fw("   :glob:\n\n")
         fw("   bpy.ops.*\n\n")
@@ -1010,7 +1018,6 @@ def rna2sphinx(BASEPATH):
         import mathutils as module
         pymodule2sphinx(BASEPATH, "mathutils", module, "Math Types & Utilities (mathutils)")
 
-
     if "mathutils.geometry" not in EXCLUDE_MODULES:
         import mathutils.geometry as module
         pymodule2sphinx(BASEPATH, "mathutils.geometry", module, "Geometry Utilities (mathutils.geometry)")
index bfe7d17fb3ac3d3840230b3945909262ec2ad71b..234627cd98a29ca68092fd6d869b4f6ef46e8404 100644 (file)
@@ -674,7 +674,7 @@ class _GenericUI:
 
     @classmethod
     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.append(draw_func)
 
index a531f1c545309be82f5669dd23ba1209c4c87d1d..750a068b09bf0f65f5699e4c1ee4863eabff9ecc 100644 (file)
@@ -348,7 +348,7 @@ class MATERIAL_PT_shading(MaterialButtonsPanel, bpy.types.Panel):
 
 class MATERIAL_PT_transp(MaterialButtonsPanel, bpy.types.Panel):
     bl_label = "Transparency"
-    bl_options = {'DEFAULT_CLOSED'}
+    bl_options = {'DEFAULT_CLOSED'}
     COMPAT_ENGINES = {'BLENDER_RENDER'}
 
     @classmethod