1
$\begingroup$

I have some code which works great at blurring the weights of the active vertex group but I'd like to make it blur ALL of them at one button press. I've edited the code to just try and show most of the function (which works -- on only the active vertex group) and my attempt at calling it in general (which SEEMS to work, at least in terms of time it takes, but at the end only the active one is blurred).

class BlurWeights( object ):
def __init__(self, active_index = None):

    obj = bpy.context.active_object
    if active_index is None:
        active_index = obj.vertex_groups.active_index

    # Get a BMesh representation to access connectivity information
    bm_obj = bmesh.new()
    bm_obj.from_mesh(obj.data)

    # Find face masking.  Faces can only be found in bmesh, however,
    # in paint mode vert selection is not updated correctly to edit mode
    # or in bmesh, so vert selection can only be found through object.data
    # I suspect this is a bug.
    face_mask = obj.data.use_paint_mask
    vertex_mask = obj.data.use_paint_mask_vertex
    if face_mask:
        masked_face_verts = set([v.index for f in bm_obj.faces if f.select for v in f.verts])

    weights = [1.0 for x in range(len(obj.data.vertices))]
    vert_indexes = []
    vert_indexes_test = []
    vert_group_indexes = []
    connected_verts = []
    gaussian_weights = []

    # Convenience function to find group index
    def vert_group_index(vert, group_id):
        for x, group_info in enumerate(vert.groups):
            if group_info.group == group_id:
                return x
        return None

    # Get weight info.  It's weird, I know
    for vert in obj.data.vertices:
        i = vert_group_index(vert, active_index)
        if i is not None:
            weights[vert.index] = vert.groups[i].weight

            # Skip if vert is not in mask.
            if not vert.hide and (not vertex_mask or vert.select) and (not face_mask or vert.index in masked_face_verts):
                # Get group info
                vert_indexes.append(vert.index)
                vert_group_indexes.append(i)
            else:
                vert_indexes_test.append(vert.index)

class WeightPaintBlurAll(bpy.types.Operator):
bl_idname = "object.weightpaint_blur_all"
bl_label = "Blur"
bl_options = {'REGISTER', 'UNDO'}

settings = bpy.props.PointerProperty(type = BlurSettingsCollection)

active_index = None
blur = None

def draw(self, context):
    row = self.layout.row()
    row.prop(self.settings, "iterations", text = "Iter")
    row = self.layout.row()
    row.prop(self.settings, "selected_only")
    # self.layout.prop(self, "factor")
    box = self.layout.box()
    row = box.row()
    row.prop(self.settings, "operation", expand = True)
    box = self.layout.box()
    row = box.row()
    row.prop(self.settings, "blur_type", expand = True)

@classmethod
def poll(cls, context):
    obj = context.active_object
    return (obj and obj.mode == 'WEIGHT_PAINT' and obj.type == 'MESH' and len(obj.vertex_groups) > 0)

def execute(self, context):
    # Lots of addons seem to disable undo during execution.  For whatever reason, 
    # I had difficulty with the undo queue reliably being turned back on, so I'm leaving this out.
    # global_undo_state = context.user_preferences.edit.use_global_undo
    # context.user_preferences.edit.use_global_undo = False

    do_gaussian = self.settings.operation == 'GAUSSIAN'

    # Initialize the blur operator if it hasn't been.
    if self.blur is None:
        self.blur = BlurWeights( self.active_index )

    self.blur.execute( iterations = self.settings.iterations, 
        factor = self.settings.factor, do_gaussian = do_gaussian,
        blur_type = int(self.settings.blur_type),
        #selected_only = self.settings.selected_only)
        selected_only = False)

    # This is a hack.  For some reason the active vertex group changes during execution,
    # Only when used from the Blur PANEL (not the regular blur buttons in the weight paint section)
    # And this seems to happen even when I touch NOTHING related to it (I tried commenting out the
    # blur operation and the active_index query in invoke).  I have...no clue.
    if self.active_index is not None:
        context.active_object.vertex_groups.active_index = self.active_index

    # context.user_preferences.edit.use_global_undo = global_undo_state
    return{'FINISHED'} 

def invoke(self, context, event):
    for key, value in context.scene.weightpaint_blur_all_settings.items():
        self.settings[key] = value
    self.active_index = context.active_object.vertex_groups.active_index
    return self.execute(context)

Let me add what I tried and perhaps it will focus things a bit better (I don't want development, I just want some guidance on how to proceed for someone who knows programming but is just learning Python).

I tried calling the function with what I thought would work, setting the active vertex group each time, like this:

if self.blur is None:
    obj = bpy.context.object
    for group in obj.vertex_groups:
        bpy.ops.object.vertex_group_set_active(group=str(group.name))
        self.blur = BlurWeights( self.active_index )

and it seems to be cycling, but not executing the function. Perhaps someone can tell me if I'm just doing THAT part right.

At this point I doubt if anyone can help me, since I now have the cycling part working (I think). Here's the right code if anyone happens to be playing at home:

obj = bpy.context.object
for group in obj.vertex_groups:
    bpy.ops.object.vertex_group_set_active(group=str(group.name))
    group_index = obj.vertex_groups[group.name].index
    print( "Group=",group.name)
    self.active_index = group_index
    self.blur = BlurWeights( self.active_index )

This appears to work (at least it does cycle through all the groups and passes the index apparently) but the call to Blurweights, although it works fine with only the active group, does not work with this. I've emailed the author but since it was written nearly 10 years ago I doubt I'll hear back (and I've reached that dreaded programmer point where I can't even think of anything else to try. Sigh).

$\endgroup$
  • $\begingroup$ Have you tried to contact the developer? Can you fix the indentation. I would vote to close this question as Mike is asking for development work and has no specifics about what is not working in the code. $\endgroup$ – J. Bakker Mar 7 '18 at 18:15
  • $\begingroup$ So I tried this: if self.blur is None: obj = bpy.context.object for group in obj.vertex_groups: bpy.ops.object.vertex_group_set_active(group=str(group.name)) self.blur = BlurWeights( self.active_index ) $\endgroup$ – Mike Kelley Mar 7 '18 at 18:56
  • $\begingroup$ Bakker -- I'm mostly just asking for some clarification -- perhaps I should phrase my question in a more general way, but all I want is to cycle through all the vertex groups. What I posted was how I was trying to do it, but it came out weird in comments, so I'll put it in the edit. $\endgroup$ – Mike Kelley Mar 7 '18 at 18:58
  • $\begingroup$ Okay, I've edited things so hopefully it just gives folks the basic idea of what I'm trying to fix. Again, not looking to get hand holding here, just a basic point me in the right direction to try and call this thing for all vertex groups. $\endgroup$ – Mike Kelley Mar 7 '18 at 19:06
  • $\begingroup$ Still the indent is incorrect. Also I miss the execute function in the blur class and the BlurSettingCollection. It is hard to point out when the posted code is not working. $\endgroup$ – J. Bakker Mar 8 '18 at 8:16
0
$\begingroup$

All good points, Bakker, and I'm sorry if I bothered you and/or anyone else in the group. As I said, I'm new to Python but not programming, and I was hoping for guidance in the right direction but should have just done my due diligence and tried to learn something about the language first.

If I can delete this question that's fine -- I did solve my problem by putting my loop inside of the invoke as well as setting self.blur (a variable tested to see if the blur needed to be run) back to none each time through the loop.

Okay -- still learning protocol here. Here's a link to the full code (which does work): https://www.dropbox.com/s/c3hdcr6ww5c7s3g/blur_all_weights.py?dl=0

It was really a matter of understanding how things are called -- I didn't examine the code enough to realize that invoke was run first and that it called execute. I'm going to assume that's either a convention or how it has to work in Python but I'll need to learn the language syntax to be sure. I was stupid enough to think that a half century of programming would allow me to easily mod this without a deep knowledge of the basic language.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.