Blender 3D: Noob to Pro/Advanced Tutorials/Python Scripting/Object, Action, Settings

Prologue
Now that we’ve covered the basics of writing a working addon and making it installable, let’s add some refinements to its functionality.

To start with, users normally add new objects via the Add menu which pops up when pressing, rather than via a custom panel. Can our script add a new “Tetrahedron” item to that menu? Yes it can.

Also, our script currently requires us to adjust its setting (the “Upside-Down” checkbox) before it performs its action. In user-interface parlance, this is the ordering “Object→Setting→Action”: select an object, specify the settings for the action, then perform the action. Whereas the preferred order in Blender 2.5x is “Object→Action→Setting”: which means the action is performed with some initial settings, which the user is then free to modify while observing their effect. This gives a much smoother workflow, rather than the user having to guess what the effects will be before applying them, and then undoing and trying again if they guessed wrong.

(Of course, since our example script creates a new object rather than modifying an existing one, the initial “Object” step is not relevant here. But it is to other operators.)

So the modifications we need to make to our script are:
 * Get rid of the existing Panel subclass.
 * Our register function will add a new entry to the Add menu (specifically, the mesh-add submenu) to invoke our Operator subclass.
 * When our operator is invoked, a panel will appear, with all the settings controls we previously had, except for the “Add” button. The user can play with these settings, and observe the effect on the newly-created object immediately.

The nice thing is, Blender does most of the hard work for us, so we only need to edit a few lines of code to achieve all the above!

Adding To The Add Menu
This needs to be done in two steps. First we need a function which will be invoked when Blender creates its Add menu: this will add an entry that invokes our custom operator by its name. Specifically, we will add our custom item to the “Mesh” submenu of the “Add” menu. We can also assign an icon to the menu item; here I’m using the generic plugin icon:

Having done that, we change our register function to add our add_to_menu item to a list that Blender uses to create its Add→Mesh menu:

Note this bpy.types.INFO_MT_mesh_add object is not currently mentioned in the API documentation; I found it by examining the scripts that come bundled with Blender.

And of course we should clean up ourselves, so the unregister function needs to remove the item we added:

Fixing Up The UI
Note that previously, our register and unregister functions were attaching a custom property to Blender’s Scene class to hold our setting value. That code is now gone, but we still need the property. Instead, it will now be attached directly to our <TT>MakeTetrahedron</TT> class.

Also, remember we said we were getting rid of the <TT>TetrahedronMakerPanel</TT> class? In fact we keep the <TT>draw</TT> method from that, and move it to our <TT>MakeTetrahedron</TT> operator, only getting rid of the last button in the panel for invoking the operator. This is because by the time the panel is being drawn, the operator has already been invoked.

We also need to tell Blender that we are conforming to the Object→Action→Settings convention, by adding the <TT>"REGISTER"</TT> item to our <TT>bl_options</TT>.

So the header of our operator class now looks like this:

Previously our custom property was called <TT>make_tetrahedron_inverted</TT>, but here I just call it <TT>inverted</TT>, because after all it is being attached to our own class, so there should be no worry about name clashes.

Note that I also changed the <TT>bl_label</TT> text, taking out the word “Add”. This is redundant, because our label will be appearing in a menu that is already titled “Add”.

<TT>invoke</TT> Versus <TT>execute</TT>
The last thing to do is rearrange the code for actually creating the tetrahedron object. Previously we had it in a method called <TT>invoke</TT>, and that method will still be called when our operator is selected from the Add→Mesh menu. Then our <TT>draw</TT> method will be called, so our panel will also appear. But then, if the user makes adjustments to the controls in our panel, Blender will invoke a different operator method, called <TT>execute</TT>.

To the user, it looks like they are making adjustments to an already-created object. But in fact our <TT>execute</TT> method can do exactly the same thing as <TT>invoke</TT>, namely create a new object every time it’s called! Blender will take care of getting rid of previously-created objects, so the user won’t know the difference.

Nice, isn’t it? But in order for this to work, our code needs to do one thing more: ensure that our newly-created object is the only selected, active object. So the following is added after <TT>NewObj</TT> has been created and linked into the scene:

Put It All Together
Note that the object-creation code has been moved into a common routine that I have called <TT>action_common</TT>. This can then be called from both the <TT>invoke</TT> and <TT>execute</TT> methods.

Undocumented Blender
That <TT>INFO_MT_mesh_add</TT> object is one of a bunch of undocumented things lurking in the <TT>bpy.types</TT> module. They all have names of the form prefix<TT>_</TT>type<TT>_</TT>restofname, where prefix is an all-uppercase mnemonic for the category of object (mostly a window type, e.g. <TT>INFO</TT> for the Info window, which is where the main menu bar appears, though there are also names for major object categories like <TT>MESH</TT> and <TT>LAMP</TT>), and type indicates the class of object: <TT>HT</TT> for a window header UI object (<TT>bpy.types.Header</TT>), <TT>MT</TT> for a menu (<TT>bpy.types.Menu</TT>), <TT>OT</TT> for an operator, and <TT>PT</TT> for a panel (<TT>bpy.types.Panel</TT>). The <TT>OT</TT> ones seem to correspond directly to objects in <TT>bpy.ops</TT>, but at a lower level; it’s probably easier just to use the official <TT>bpy.ops</TT> objects. As for the <TT>HT</TT>, <TT>MT</TT> and <TT>PT</TT> objects, these all have <TT>append</TT>, <TT>prepend</TT> and <TT>remove</TT> methods that you can use to customize them: <TT>append</TT> adds a widget at the end (right or bottom), while <TT>prepend</TT> puts it at the front (left or top).

Since they aren’t (yet) mentioned in the official Blender documentation, here is a list of all the ones I’ve been able to find: