Category: Blender

How To Make My Addon Support “Undo” In Blender

GOAL

Today’s goal is to implement “Undo” in my Blender addon.

Environment

Blender 2.83
Windows10

Method

Add bl_options “UNDO” or “UNDO_GROUPED” into the your custom operator. It’s easy.

UNDO Undo: Push an undo event (needed for operator redo).
UNDO_GROUPED: Grouped Undo, Push a single undo event for repeated instances of this operator.

from Blender2.93.2 Python API Documentation

The following is an example.

class ADDMATRIX_add_cube(bpy.types.Operator):
    bl_idname = 'add_matrix_obj.add_cube'
    bl_label = "Add matrix cube"
    bl_options = {'REGISTER', "UNDO"} # Add "UNDO" option here

    input1: bpy.props.IntProperty()
    input2: bpy.props.IntProperty()

    def execute(self, context):
        for xi in range(self.input1):
            x = xi*1.2
            for yi in range(self.input2):
                y = yi*1.2
                bpy.ops.mesh.primitive_cube_add(size=0.5, enter_editmode=False, align='WORLD', location=(x, y, 0))
        return {'FINISHED'}

Postscript

be aware of the error where Blender crashed when undo after executing script in python console. (Reference: Fix T86293: crash undoing after executing the python console in certain)

That was fixed in Blender 2.93, the latest version. And You can fix the error by adding bl_options “UNDO_GROUPED” into the ConsoleExec operation in C:/Program Files/Blender Foundation/Blender 2.83/2.83/scripts/startup/bl_operators/console.py.

class ConsoleExec(Operator):
    """Execute the current console line as a python expression"""
    bl_idname = "console.execute"
    bl_label = "Console Execute"
    bl_options = {"UNDO_GROUPED"} # add undo here

    interactive: BoolProperty(
        options={'SKIP_SAVE'},
    )

    @classmethod
    def poll(cls, context):
        return (context.area and context.area.type == 'CONSOLE')

How To Get Icon List In Blender

GOAL

Today’s goal is to show Icon Viewer that shows the list of icon we can use in Blender and use it.

Icon Viewer

Environment

Blender2.83(LTS)
Windows10

Method

1. Activate addon “Development Icon Viewer”

2. Open Icon Viewer

Open text Editor and click “Dev” tab.

Click on any icon you like and it’s name will be copied to the clipboard. Then paste it into your editor.

Examples

1. Operator button

def draw(self, context):
    layout = self.layout
    col = layout.column(align=True)
    col.operator("mesh.primitive_monkey_add", icon="MONKEY")

2. EnumProperty

class MyProp(bpy.types.PropertyGroup):
    items = [("id1", "name1", "description 1", "HELP", 0),
             ("id2", "name2", "description 2", "GHOST_ENABLED", 1),
             ("id3", "mame3", "description 3", "FUND", 2),]
    enum: bpy.props.EnumProperty(items=items)
def draw(self, context):
    layout = self.layout
    col = layout.column(align=True)
    col.prop(context.scene.MyProp, "enum")

How To Pass Arguments To Custom Operator In Blender Python

GOAL

Today’s goal is to create operator that takes arguments in Blender Python.

The following is a custom operator that takes 2 argument “count_x” and “count_y”, and add cube object in the form of count_x rows and count_y columns.

Environment

Blender 2.83(LTS)
Windows10

Method

1. Create operator with properties

Pass the argument values through the properties to custom operator. Use “:” to add property in Blender custom operator as below. Check “Operator Example” in Blender manual for details.

class ADDMATRIX_add_cube(bpy.types.Operator):
    bl_idname = 'add_matrix_obj.add_cube'
    bl_label = "Add matrix cube"
    bl_options = {'REGISTER', "UNDO"}

    input1: bpy.props.IntProperty() # add argument1 as property "input1"
    input2: bpy.props.IntProperty() # add argument2 as property "input2"

    def execute(self, context):
        for xi in range(self.input1):
            x = xi*1.2
            for yi in range(self.input2):
                y = yi*1.2
                bpy.ops.mesh.primitive_cube_add(size=0.5, enter_editmode=False, align='WORLD', location=(x, y, 0))
        return {'FINISHED'}

You can execute operator with passing values of arguments “input1” and “input2” as below.

(more…)

List Of Blender Properties

GOAL

Today’s goal is to list up property UI that can be used in Blender Addon.
Reference: Property Definitions

Environment

Blender 2.83(LTS)
Windows10

List of Properties

  • BoolProperty
  • BoolVectorProperty
  • CollectionProperty
  • EnumProperty
  • FloatProperty
  • FloatVectorProperty
  • IntProperty
  • IntVectorProperty
  • PointerProperty
  • StringProperty

How each property looks like

BoolProperty

bool: bpy.props.BoolProperty()

BoolVectorProperty

bool_vector: bpy.props.BoolVectorProperty()

subtypes of BoolVectorProperty

The UI style or label differs according to its subtype.

bool_vector_color: bpy.props.BoolVectorProperty(subtype='COLOR')
bool_vector_translation: bpy.props.BoolVectorProperty(subtype='TRANSLATION')
# and more...

CollectionProperty

UILayout.prop() just shows the number of CollectionProperty. (Use “template_list” to show the item list of CollectionProperty)

class PropertySetTest(bpy.types.PropertyGroup):
    test_int: bpy.props.IntProperty()
    test_string: bpy.props.StringProperty()
collection: bpy.props.CollectionProperty(type=PropertySetTest)

EnumProperty

items = [("id1", "name1", "description 1", "MESH_CUBE", 0),
         ("id2", "name2", "description 2", "MESH_TORUS", 1),
         ("id3", "mame3", "description 3", "CONE", 2),]
enum: bpy.props.EnumProperty(items=items)

FloatProperty

float: bpy.props.FloatProperty()

subtypes of FloatProperty

float_pixel: bpy.props.FloatProperty(subtype="PIXEL")
float_unsigned: bpy.props.FloatProperty(subtype="UNSIGNED")
# and more...

units of FloatProperty

float_none: bpy.props.FloatProperty(unit='NONE')
float_length: bpy.props.FloatProperty(unit='LENGTH')
# and more...

FloatVectorProperty

float_vector: bpy.props.FloatVectorProperty()

subtypes of FloatVectorProperty

units of FloatVectorProperty

IntProperty

subtypes of IntVectorProperty

IntVectorProperty

subtypes of FloatVectorProperty

PointerProperty

PointerProperty takes argument ‘type’ to set the type of pointer.

pointer_scene: bpy.props.PointerProperty(type=bpy.types.Scene)
pointer_object: bpy.props.PointerProperty(type=bpy.types.Object)
pointer_matarial: bpy.props.PointerProperty(type=bpy.types.Material)
pointer_mesh: bpy.props.PointerProperty(type=bpy.types.Mesh)
pointer_image: bpy.props.PointerProperty(type=bpy.types.Image)
pointer_brush: bpy.props.PointerProperty(type=bpy.types.Brush)
pointer_camera: bpy.props.PointerProperty(type=bpy.types.Camera)
pointer_light: bpy.props.PointerProperty(type=bpy.types.Light)

StringProperty

subtypes of FloatVectorProperty

How to Display Custom Property on Properties Editor in Blender Addon

GOAL

Today’s goal is to develop an addon and display custom property on Properties editor. For example, add “target_obj” property to bpy.types.Object for management and edit the value of “target_obj” with Properties editor.

Environment

Blender2.83(LTS)
Windows10

Method

1. Register property

The following is the code of addon just add the property “target_obj” to bpy.types.Object.

bl_info = {
        "name": "Add Target Property",
        "description": "test addon to add target_obj properties to Object",
        "version": (1, 0),
        "blender": (2, 80, 0),
        "category": "3D View",
        }
import bpy

def register():
    bpy.types.Object.target_obj = bpy.props.PointerProperty(name="TargetObj", type=bpy.types.Object)

def unregister():
    del bpy.types.Object.target_obj

if __name__ == '__main__':
    register()

At this time, the property “target_obj” is not displayed in Properties Editor.

Properties Editor
(more…)

How To Detect Object Moving In Blender Addon.

GOAL

Today’s goal is to develop addon to detect object’s property changes and call function. In my case, I created an addon to detect object moving and display the message “object <Name> moving” to the system console.

Environment

Blender 1.83(LTS)
Windows10

Method

Use handler

Use Application Handlers to call function by triggers. I used “bpy.app.handlers.depsgraph_update_post” for calling the function to check if the current object is moving in each time when the blender scene has changed.

Reference: Persistent Handler Example

import bpy
from bpy.app.handlers import persistent

@persistent
def obj_init(scene):
    # check if the bpy.context.object is moving or not

bpy.app.handlers.depsgraph_update_post.append(obj_init)

Add property to store location before move

I added property “before_loc” to compare current object location to the one before depsgraph updated. If the location changed, store new location to the property “before_loc”.

(more…)

How to Add Properties In Your Blender Addon UI

GOAL

Today’s goal is to develop 3 types of Blender addons with properties, showing properties in Panel or Properties Editor as below.

Note: Addons in this article are the simplest examples and not practical.

Environment

Blender 2.83(LTS)
Windows10

What is property?

At first, I recommend to read “Properties” in Blender manual and “Property Definitions (bpy.props)” in Blender Python API documentation. And check “Operator Property” for details about how to use properties in Addon.

Properties

The Properties shows and allows editing of many active data, including the active scene and object.

from Blender 2.93 Manual

Property has information about any type of data such as Object, Material, Scene and so on. For example, Objects have properties such as “location”, “scale”, “active_material”, “animation_data”, “bound_box” and more. (Be careful that some of them are internal or non-editable!) You can add custom properties to the existing data type or create property group in Addon.

Example of properties

1. Change Existing property

Change the value stored in Blender data with UI. For example, change the size of the object, change the color of material.

bl_info = {
        "name": "Existing Property Test",
        "description": "test addon to change existing property",
        "version": (1, 0),
        "blender": (2, 83, 0),
        "category": "3D View",
        }
import bpy

class PROPTEST_PT_my_panel(bpy.types.Panel):
    bl_label = "Prop Test Addon1"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'

    def draw(self, context):
        layout = self.layout
        col = layout.column(align=True)
        # existing property
        if "Cube" in bpy.data.objects:
            col.prop(bpy.data.objects["Cube"], "scale")
            
            active_material = bpy.data.objects["Cube"].active_material
            col.prop(active_material, "diffuse_color")
            
        if "Light" in bpy.data.lights:
            col.prop(bpy.data.lights["Light"], "type")

def register():
    bpy.utils.register_class(PROPTEST_PT_my_panel)
def unregister():
    bpy.utils.unregister_class(PROPTEST_PT_my_panel)

if __name__ == '__main__':
    register()
(more…)

How To Unfold UV with Seams in Blender

GOAL

Today’s goal is to summarize the way to unfold UV in Blender by making seams as below.

Environment

Windows10
Blender2.83

Method

The model to unfold UV

1. Make Seams

Select elements (vertices, edges or faces) in EDIT mode and click UV > Make Seams. It is better to make seams in the places that is hidden from viewers such as under the arms or inside of the legs. And separate faces if the part has clear boundaries.

(more…)

Trying to fix Blender “Mirror” bug

The behavior of Mirror in blender is for some reason. So I tried to fix it by myself. I do not take any responsibility for any damage or loss if you follow this article.

Problem

I don’t know why but object menu “Mirror > X Local” calls operation of “X Global” in my blender 2.83 on windows10. Actually this problem can be solved by update and re-install blender 2.83.

# the same function is called as below
bpy.ops.transform.mirror(orient_type='GLOBAL', constraint_axis=(True, False, False), use_proportional_edit=False, proportional_edit_falloff='SMOOTH', proportional_size=1, use_proportional_connected=False, use_proportional_projected=False)

What I did to fix

I searched word “Mirror” in C:/Program Files/Blender Foundation/Blender 2.83/2.83 and find the definition of mirror menu in C:/Program Files/Blender Foundation/Blender 2.83/2.83/scripts/startup/bl_ui/space_view3d.py.

class VIEW3D_MT_mirror(Menu):
    bl_label = "Mirror"

    def draw(self, _context):
        layout = self.layout

        layout.operator("transform.mirror", text="Interactive Mirror")

        layout.separator()

        layout.operator_context = 'EXEC_REGION_WIN'

        for (space_name, space_id) in (("Global", 'GLOBAL'), ("Local", 'LOCAL')):
            for axis_index, axis_name in enumerate("XYZ"):
                props = layout.operator("transform.mirror", text=f"{axis_name!s} {space_name!s}")
                props.constraint_axis[axis_index] = True
                props.orient_type = 'GLOBAL' # the point that cause a problem!!!

            if space_id == 'GLOBAL':
                layout.separator()

So change ”props.orient_type = ‘GLOBAL’” into “props.orient_type = space_id” and save with administrative privileges.

Restart Blender and check the result.

bpy.ops.transform.mirror(orient_type='LOCAL', constraint_axis=(True, False, False), use_proportional_edit=False, proportional_edit_falloff='SMOOTH', proportional_size=1, use_proportional_connected=False, use_proportional_projected=False)

It was easy to modify blender program in python level.