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.
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()
The following is the panel UI for properties.
col = layout.column(align=True) if "Cube" in bpy.data.objects: col.prop(bpy.data.objects["Cube"], "scale") # Scale of obj active_material = bpy.data.objects["Cube"].active_material col.prop(active_material, "diffuse_color") # Diffuse Color of material if "Light" in bpy.data.lights: col.prop(bpy.data.lights["Light"], "type") # Type of light
Properties
You can see the properties of each data type in blender python API documentation as below.
2. Add property to the existing data
This is the method of adding custom property to the existing data such as object, material and so on.
def register(): bpy.types.<TYPE>.prop_name1 = bpy.props.TypeOfProperty() bpy.types.<TYPE>.prop_name2= bpy.props.TypeOfProperty() def unregister(): del bpy.types.<TYPE>.prop_name1 del bpy.types.<TYPE>.prop_name2
For example, the following is an addon to show the distance from current object to target. A new property “target_obj” and non-editable “distance” are added to bpy.types.Object.
The following is the source code.
bl_info = { "name": "Add Property Test", "description": "test addon to add properties to Object", "version": (1, 0), "blender": (2, 80, 0), "category": "3D View", } import bpy import math from bpy.app.handlers import persistent def distance(pos1, pos2): return math.sqrt((pos1.x-pos2.x)**2+(pos1.y-pos2.y)**2+(pos1.z-pos2.z)**2) @persistent def distance_update(scene): if hasattr(bpy.context.object, "target_obj"): target_obj = bpy.context.object.target_obj if target_obj: target_pos = target_obj.location current_pos = bpy.context.object.location new_distance = distance(target_pos, current_pos) if bpy.context.object.distance != new_distance: bpy.context.object.distance = distance(target_pos, current_pos) else: bpy.context.object.target_obj = None bpy.context.object.distance = 0 bpy.app.handlers.depsgraph_update_post.append(distance_update) def register(): bpy.types.Object.target_obj = bpy.props.PointerProperty(name="TargetObj", type=bpy.types.Object) # add prop "target_obj" bpy.types.Object.distance = bpy.props.FloatProperty(name="Distance") # add prop "distance" def unregister(): del bpy.types.Object.target_obj del bpy.types.Object.distance if __name__ == '__main__': register()
Use the value of properties in custom operator
If you’d like to use properties in global not in each objects, use bpy.types.Scene instead. If you’d like to use values of properties in custom operator, see “How To Pass Arguments To Custom Operator In Blender Python”.
# add parameter "count_x" and "count_y" to bpy.types.Scene bpy.types.Scene.count_x = bpy.props.IntProperty() bpy.types.Scene.count_y = bpy.props.IntProperty()
3. Custom property group
If there are many properties or set of properties, it is better to use property group instead of properties registered independently. This is the method of adding, setting and getting values of property group.
class CusomPropGroup(bpy.types.PropertyGroup): # define properties here # prop_name1: bpy.props.TypeOfProperty() # prop_name2: bpy.props.TypeOfProperty() def register(): bpy.utils.register_class(CusomPropGroup) bpy.types.Scene.custom_prop_name= bpy.props.PointerProperty(type=CusomPropGroup) def unregister(): bpy.utils.unregister_class(CusomPropGroup) del bpy.types.Scene.custom_prop_name
Th following code is the example addon using custom property group.
import bpy class PROPTEST3_add(bpy.types.Operator): bl_idname = 'color_obj.add' bl_label = "Add Color Obj" bl_options = {'REGISTER'} def execute(self, context): obj_type = context.scene.colorObjProp.object_select for i in range(5): color = getattr(context.scene.colorObjProp, "color_id{}".format(i)) mat = bpy.data.materials.new("test_material") mat.diffuse_color = list(color) + [1.0] x = 2.0 + 2.0 * i if obj_type == "CUBE": obj = bpy.ops.mesh.primitive_cube_add(enter_editmode=False, align='WORLD', location=(x, 0, 0)) elif obj_type == "SPHERE": obj = bpy.ops.mesh.primitive_uv_sphere_add(enter_editmode=False, align='WORLD', location=(x, 0, 0)) bpy.context.object.data.materials.append(mat) return {'FINISHED'} class PROPTEST3_PT_panel(bpy.types.Panel): bl_label = "Add Color Object" bl_space_type = 'VIEW_3D' bl_region_type = 'UI' def draw(self, context): layout = self.layout col = layout.column() col.prop(context.scene.colorObjProp, "object_select") row = col.row() for i in range(5): row.prop(context.scene.colorObjProp, "color_id{}".format(i), text="") col.operator("color_obj.add", text="Add Colored Obj") class colorObjProp(bpy.types.PropertyGroup): # add property group obj_items = [ ("CUBE", "Cube", '', 'MESH_CUBE', 0), ("SPHERE", "Sphere", '', 'MESH_UVSPHERE', 1), ] object_select: bpy.props.EnumProperty( items=obj_items, default="CUBE" ) def get_color(i): return bpy.props.FloatVectorProperty( name="Color", description="Set Color for the Palette", subtype="COLOR", default=(0.5, 0.2 * i, 0.5), max=1.0, min=0.0 ) color_id0: get_color(0) color_id1: get_color(1) color_id2: get_color(2) color_id3: get_color(3) color_id4: get_color(4) classes = [ PROPTEST3_add, PROPTEST3_PT_panel ] def register(): bpy.utils.register_class(colorObjProp) bpy.types.Scene.colorObjProp = bpy.props.PointerProperty(type=colorObjProp) for c in classes: bpy.utils.register_class(c) def unregister(): bpy.utils.unregister_class(colorObjProp) del bpy.types.Scene.colorObjProp for c in classes: bpy.utils.unregister_class(c) if __name__ == '__main__': register()
The following is panel of this addon.
When click “Add Colored Obj” button, 5 colored objects are added to the scene.