5
$\begingroup$

Looking at the API docs I found VertexGroup.add(index, weight, type), but It cannot be called in Edit Mode. When I try to call it, I always get:

RuntimeError: VertexGroup.add(): cannot be called while object is in edit mode.

The buttons in the vertex group list use bpy.ops.objects.vertex_group_assign(), but to work with it you have to manipulate selections, this is not nice to do from a script.


Why I try to use VertexGroup.add() in Edit Mode? My script works this way:

Human weight paint a bone, "Arm_1.L", "Arm_1.L" shares vertices with "Arm_2.L" in this case they share half the vertices (4 bone arm rig, trick to have nice deform in certain movements). When script run, all selected vertices that exist in both groups (Arm_1.L and Arm_2.L) will copy the weights of Arm_1.L into Arm_2.L but inverted. This way, I only need to weight paint "Arm_1.L" and "Arm_3.L", the script will fill the weights for "Arm_2.L".

import bpy

# ensure we are in edit mode
bpy.ops.object.mode_set(mode="EDIT")

# configurable parameters
src_group = "Arm_03.L"
dst_group = "Arm_02.L"

obj = bpy.context.edit_object
src_group_index = obj.vertex_groups[src_group].index
dst_group_index = obj.vertex_groups[dst_group].index

mesh = obj.data
sel_verts = [v for v in mesh.vertices if v.select]
#sel_verts = mesh.vertices

# SEGFAULT in this line
bpy.ops.object.mode_set(mode="OBJECT")

print("Working")

for v in sel_verts:
    v1in = False
    v2in = False
    for g in v.groups:
        if g.group == src_group_index:
            v1in = True
        if g.group == dst_group_index:
            v2in = True
    if v1in and v2in:
        print("True")
        obj.vertex_groups[dst_group].add([v.index], 1.0 - obj.vertex_groups[src_group].weight(v.index), 'REPLACE')

print("Finished")
$\endgroup$
  • 2
    $\begingroup$ Why do you want to call this from edit mode? VertexGroup.add(index, weight, type) works well in object mode and weight paint mode. If you know the indices of verts you want to add (which you can find out in edit mode and save in a list for later), you can go back to object mode and set the weight via VertexGroup.add. $\endgroup$ – TLousky Oct 7 '15 at 19:40
  • $\begingroup$ @TLousky Maybe I don't need to. I tried to use bpy.ops.object.mode_set() to go to "OBJECT" but for some reason Blender 2.76R3 is crashing during mode_set call. May that be because I trying to access vertex data collected int "EDIT" mode? I will update question with my code, just a few lines. $\endgroup$ – Hatoru Hansou Oct 8 '15 at 0:12
5
$\begingroup$

I made two small changes to your script that might help it work as intended.

  1. I switched to using bmesh to save the selected verts data. I find it a bit more reliable and stable than the built in mesh data.
  2. I saved the vertex indices instead of references to the vertices themselves in the list comprehension. This makes sure the list, which is comprised of simple integers, will persist after you go back to object mode (bmesh vertex references would have been destroyed). You don't need anything except the indices for this script anyway. This could have been what crashed your script, not sure but you'll have to test and let us know.

So here's the updated code [EDIT: the previous code failed because it was impossible to access v.groups once we only saved vertex indices and not vertex references. The script needed to decide if it transfers and inverts a weight for a given vertex. The following is based on the previous idea, with changes from Hatrou Hansou, and works OK]:

import bpy

# ensure we are in edit mode
bpy.ops.object.mode_set(mode="EDIT")

# configurable parameters
src_group = "Arm_03.L"
dst_group = "Arm_02.L"

obj = bpy.context.edit_object
src_group_index = obj.vertex_groups[src_group].index
dst_group_index = obj.vertex_groups[dst_group].index

mesh = obj.data

# Saving vertex indices only, instead of vertex objects in this list
# Copy v.groups.group, do not reference v.groups (unstable)
sel_verts_ig = [[v.index, [g.group for g in v.groups]] for v in mesh.vertices if v.select]

# no more SEGFAULT in this line
bpy.ops.object.mode_set(mode="OBJECT")

print("Working")

for ig in sel_verts_ig:
    v1in = False
    v2in = False
    i = ig[0]
    for g in ig[1]:
        if g == src_group_index:
            v1in = True
        if g == dst_group_index:
            v2in = True
    if v1in and v2in:
        obj.vertex_groups[dst_group].add([i], 1.0 - obj.vertex_groups[src_group].weight(i), 'REPLACE')

print("Finished")
$\endgroup$
  • $\begingroup$ You was right about the segfault being caused by references to things only accessible in edit mode when the script try to go to object mode. But your original code still failed for me because the vertex groups are also needed. I edited your answer, please review edit. $\endgroup$ – Hatoru Hansou Oct 8 '15 at 14:46
  • $\begingroup$ Approved your edit, glad it worked. $\endgroup$ – TLousky Oct 8 '15 at 14:48

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.