2
$\begingroup$

I am writing an add-on that creates a mesh with several randomly generated aspects. The mesh is generated from default property values when the operator is initially called. These properties are added to the tools panel for the user to modify.

In some cases I want to modify the already generated mesh both to avoid wasted recalculation and to retain the majority of randomly generated features. Ie - it might look the way I want it except for some minor aspect I can change without effecting the overall appearance.

To do this, my property update functions set a flag to indicate whether the property change necessitates a complete regeneration of the mesh or if the existing mesh can be tweaked instead.

I am seeing that there is no object to tweak when the operator's execute function is called after a property change. It appears that an undo is performed before the operator is called again.

Is there a way to avoid the undo operation? Or a way to store the mesh and access it when the operator is called again?

As an example:

# ------------------------------------------------------------
# Operator for adding a Thing
# ------------------------------------------------------------
class ThingAddOp(bpy.types.Operator):

    ...

    # set a flag indicating a complete regenerate is required
    def update_regenerate(self, context):
        self.do_regenerate = True

    # set a flag to indicating a simpler update can be done
    def update_modify(self, context):
        self.do_modify = True

    # flags used to determine if Thing should be regenerated or modified
    do_regenerate = BoolProperty(name='Must regenerate object', default=False, options={'HIDDEN'})
    do_modify     = BoolProperty(name='Can modify object', default=False, options={'HIDDEN'})

    ...

    def execute(self, context):
        thing_add(self)
        self.do_regenerate = False
        self.do_modify = False
        return {'FINISHED'}

    @classmethod
    def poll(cls, context):
        return context.mode == 'OBJECT' 

    def invoke(self, context, event):
        return self.execute(context)

    def draw(self, context):
        ...

# ------------------------------------------------------------
# Creates a random Thing
# ------------------------------------------------------------
def thing_add(props):
    if props.do_modify:
        # At this point there is no object to modify!!
        bpy.ops.object.mode_set(mode='EDIT', toggle=False)

        # only resize because the user liked the randomly 
        # generated object but wants to resize the base
        # without changing the other apsects of it
        size_base(props)
    else:
        create_base(props)   # creates a base with a given size
        create_spokes(props) # creates randomly oriented spokes
    bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
$\endgroup$
1
  • 2
    $\begingroup$ Blender's redo panel will revert all changes every time and re-run the operation, there's no way around that. The behavior might be different / more controllable when writing a modifier in C, I'm not sure. $\endgroup$
    – CodeManX
    Commented Aug 24, 2014 at 1:19

1 Answer 1

2
$\begingroup$

Operators automatically push their actions containing the properties used onto the undo/redo stack, by calling an operator with different options you are adding it to the stack each time.

To prevent calling the operator each time an option changes you could move the properties for the options to outside of the operator, maybe to an AddonPreferences instance? This can then be accessed by the operator and a panel or directly in the User Preferences when enabling the addon. See Cloud Generator addon for an example of properties added to bpy.types.Scene and used by both panel and operator.

You could also try having the operator take a single argument as a string (comma separated list you can split?), a CollectionProperty or BoolVectorProperty, which I think would integrate better with the undo stack. A panel displaying the operators options could then combine the options into one property to be passed to the operator call.

Having said that, you may also find that calling the operator with defaults and only allowing the user to change options within the Redo Last to be a workable solution. See the sapling addon and it's large array of options only available in the redo last. Also the space tree addon in contrib has a similarly large list of options. You can adjust properties in the redo last panel, see the results and only have one undo step.

$\endgroup$
1
  • $\begingroup$ Thank you for the thorough response. I appreciate the effort. I'll look deeper into your suggestions to see if there is a manageable way to get the behavior I want. I am beginning to think using a modifier to make additional changes after the mesh has been added by the operator might be a better approach. $\endgroup$
    – Mike N.
    Commented Aug 24, 2014 at 19:14

You must log in to answer this question.

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