I would like to duplicate, for example, a cylinder with its number of vertices stored in its properties, and tweak this value if needded.

To do this, I click on a gizmo, and a new cylinder is created, with the stored value of the previous cylinder. But when I want to change this value within the property panel, the value remains the same.

How can I change it?

Here is an example:

import bpy
from mathutils import Matrix

bpy.ops.mesh.primitive_cylinder_add(vertices = 12, enter_editmode=False, align='WORLD', location=(-2, 0, 0))
obj = bpy.context.object
obj["vertices"] = 12

class CylinderWidgetGroup(bpy.types.GizmoGroup):
    bl_idname = "OBJECT_GGT_cylinder_tweak"
    bl_label = "cylinder tweaker"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'WINDOW'
    bl_options = {'3D', 'PERSISTENT'}

    def poll(cls, context):
        obj = context.object
            if "Cylinder" in obj.name:
                goodName = obj.name

            return (obj and obj.name == goodName)
            return(obj and False)

    def setup(self, context):

        obj = context.object
        mpr = self.gizmos.new("GIZMO_GT_arrow_3d")
        mpr.draw_style = 'BOX'

        self.gizmoWidget = mpr

    def refresh(self, context):
        obj = context.object
        mpr = self.gizmoWidget
        props = mpr.target_set_operator("object.cylinder_update")
        posMatrix = Matrix( ((1,0,0,obj.location.x),(0,1,0,obj.location.y),(0,0,1,obj.location.z),(0,0,0,1)) ) 
        mpr.matrix_basis = posMatrix

class CylinderUpdate(bpy.types.Operator):
    bl_idname = "object.cylinder_update" # has to be all lower case
    bl_label = "Cylinder Update"
    bl_options = {'REGISTER', 'UNDO'} 

    vertices : bpy.props.IntProperty(name="vertices", default=5, min=3, max=100)

    def execute(self, context):
        obj = context.object
        self.vertices = obj["vertices"]
        bpy.ops.mesh.primitive_cylinder_add(vertices = obj["vertices"], enter_editmode=False, align='WORLD', location=(0, 0, 0))
        obj = context.object
        obj["vertices"] = self.vertices
        return {'FINISHED'}


1 Answer 1


Set verts in invoke method

Set the value when the operator is invoked (run once). Give the new cylinder the operators value for self.vertices.

Demonstration of that change

enter image description here

    def invoke(self, context, event):
        obj = context.object
        self.vertices = obj["vertices"]
        return {'FINISHED'}
    def execute(self, context):        
            align='WORLD', location=(0, 0, 0))
        obj = context.object
        obj["vertices"] = self.vertices
        return {'FINISHED'}

Some notes.

To match new cylinder transform to old would

mw = context.object.matrix_world.copy()

before new cylinder op, and assign it to new cylinder.

The poll method could be

def poll(cls, context):
    ob = context.object
    return ob and ob.name.startswith("Cylinder")

The poll method gets fired off a lot. Good practice not to use a try clause here.

The gizmo matrix basis could be


However this is using local coords to make a global matrix and will result in "losing touch" with the cylinder if parenting or constraints are used to locate it. Worth investigating using


Automatically set the custom property when cylinder added

Check out the way this answer https://blender.stackexchange.com/a/163258/15543 manipulates the newly created mesh primitive. Recommend doing something similar here.

When the active operator is the add prim cylinder add the custom property. In this case could poll for "vertices" in ob.keys() (Maybe change name to cylinder_vertices)

  • 1
    $\begingroup$ Invoke is exactly what I need for my project, and great thanks for the other tips, batFINGER ! $\endgroup$
    – Tolgan
    Commented Jun 7, 2020 at 14:21

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .