Blender 3D: Noob to Pro/Advanced Tutorials/Blender Scripting/Adding A Custom Property

From Wikibooks, open books for an open world
Jump to navigation Jump to search

Addons will commonly do more than unconditionally perform a single action; the user may be able to control their actions in various ways. We can add controls for adjusting settings to our addon panel, and associate these with custom properties that we define for the current scene; Blender will then take care of updating the property values as the user operates those controls, and our operator’s invoke routine can fetch those property values when it runs.

Defining A Property[edit | edit source]

Properties need to be defined at the same time our custom classes get registered. We previously did this in top-level statements, but now let’s gather it all together into a register method, like this:

def register() :
    bpy.utils.register_class(MakeTetrahedron)
    bpy.utils.register_class(TetrahedronMakerPanel)
    bpy.types.Scene.make_tetrahedron_inverted = bpy.props.BoolProperty \
      (
        name = "Upside Down",
        description = "Generate the tetrahedron upside down",
        default = False
      )
#end register

Here we are attaching the property as a new attribute of Blender’s Scene class; Python lets us assign new attributes to just about any object, and Blender takes full advantage of that. Note that the property must be created using one of the property-definition routines provided in bpy.props: choose the one that matches the type of property you want to create. Here we are defining a simple true/false toggle, which the user will control via a checkbox. Whatever name you use for your custom class attribute, an instance of that class will have an attribute with the same name, holding the actual value for that property.

The name will be used as the name of a control for examining or changing this property, while the description will appear as a tooltip when the user hovers the mouse over the control. The default is used as the initial value of the property.

Note also I tried to use a name, make_tetrahedron_inverted, which is less likely to clash with names defined by other addons or parts of Blender.

Let’s also add an unregister method, which undoes everything that register does. This won’t actually be used for now, but it will become relevant later when we extract the addon for separate installation:

def unregister() :
    bpy.utils.unregister_class(MakeTetrahedron)
    bpy.utils.unregister_class(TetrahedronMakerPanel)
    del bpy.types.Scene.make_tetrahedron_inverted
#end unregister

To make the checkbox appear, add the following line to our panel’s draw routine:

       TheColumn.prop(context.scene, "make_tetrahedron_inverted")

The first argument to the prop method must be an instance of the class to which we attached our property definition above; in this case, it is the current scene.

Finally, we need to finish off our script with the following bit of boilerplate which will invoke our registration routine in the situations (like the Text Editor) where Blender doesn't do it for us:

if __name__ == "__main__" :
    register()
#end if

Using The Property[edit | edit source]

Now we actually need to use the property in our operator’s execute routine. We’ll use it to negate a scale factor which will be applied to the Z coordinate of the vertices of the tetrahedron:

        Scale = -1 if context.scene.make_tetrahedron_inverted else 1

But only the last vertex has a nonzero Z coordinate, so this is the only one that needs a change in its computation:

        Vertices = \
          [
            mathutils.Vector((0, -1 / math.sqrt(3),0)),
            mathutils.Vector((0.5, 1 / (2 * math.sqrt(3)), 0)),
            mathutils.Vector((-0.5, 1 / (2 * math.sqrt(3)), 0)),
            mathutils.Vector((0, 0, Scale * math.sqrt(2 / 3))),
          ]

Put It All Together[edit | edit source]

To recap all our changes, here is the complete updated script:

import math
import bpy
import mathutils

class TetrahedronMakerPanel(bpy.types.Panel) :
    bl_space_type = "VIEW_3D"
    bl_region_type = "TOOLS"
    bl_context = "objectmode"
    bl_category = "Create"
    bl_label = "Add Tetrahedron"

    def draw(self, context) :
        TheColumn = self.layout.column(align = True)
        TheColumn.prop(context.scene, "make_tetrahedron_inverted")
        TheColumn.operator("mesh.make_tetrahedron", text = "Add Tetrahedron")
    #end draw

#end TetrahedronMakerPanel

class MakeTetrahedron(bpy.types.Operator) :
    bl_idname = "mesh.make_tetrahedron"
    bl_label = "Add Tetrahedron"
    bl_options = {"UNDO"}

    def invoke(self, context, event) :
        Scale = -1 if context.scene.make_tetrahedron_inverted else 1
        Vertices = \
          [
            mathutils.Vector((0, -1 / math.sqrt(3),0)),
            mathutils.Vector((0.5, 1 / (2 * math.sqrt(3)), 0)),
            mathutils.Vector((-0.5, 1 / (2 * math.sqrt(3)), 0)),
            mathutils.Vector((0, 0, Scale * math.sqrt(2 / 3))),
          ]
        NewMesh = bpy.data.meshes.new("Tetrahedron")
        NewMesh.from_pydata \
          (
            Vertices,
            [],
            [[0, 1, 2], [0, 1, 3], [1, 2, 3], [2, 0, 3]]
          )
        NewMesh.update()
        NewObj = bpy.data.objects.new("Tetrahedron", NewMesh)
        context.scene.objects.link(NewObj)
        return {"FINISHED"}
    #end invoke

#end MakeTetrahedron

def register() :
    bpy.utils.register_class(MakeTetrahedron)
    bpy.utils.register_class(TetrahedronMakerPanel)
    bpy.types.Scene.make_tetrahedron_inverted = bpy.props.BoolProperty \
      (
        name = "Upside Down",
        description = "Generate the tetrahedron upside down",
        default = False
      )
#end register

def unregister() :
    bpy.utils.unregister_class(MakeTetrahedron)
    bpy.utils.unregister_class(TetrahedronMakerPanel)
    del bpy.types.Scene.make_tetrahedron_inverted
#end unregister

if __name__ == "__main__" :
    register()
#end if

As before, execute the script with  ALT + P . Check the Tool Shelf in the 3D View, and your panel should now look like this:

Try executing it with and without the checkbox checked, and you should end up with two tetrahedra pointing in opposite directions.