How to set vertex weights using Blender's Python API? 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")
I made two small changes to your script that might help it work as intended.


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.
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")