Creating shape keys using Python

I want to use the Python API to create a sequence of absolute shape keys that deform a mesh over time, however I'm having a lot of trouble figuring out the proper way to do that.

Currently I'm trying to mimic how I would do it by hand, i.e. using bpy.ops, which I'm sure isn't the best way to begin with, but even then I can't get it to work. A minimal sample of what I'm trying to do:

import bpy

if __name__ == '__main__':
# Create nx*ny points as a starting mesh
nx = 11
ny = 11
data = [(x/10., y/10., (x**2+y**2)/200.) \
for x in range(nx) for y in range(ny)]

# Create edge loops to define faces
faces = []
count  = 0
for i in range(ny*(nx-1)):
if count < ny-1:
faces.append((i, i+1, i+ny+1, i+ny))
count += 1
else:
count = 0

# Create mesh and object
mesh = bpy.data.meshes.new('mesh')
obj = bpy.data.objects.new('obj', mesh)
obj.location = (0, 0, 0)
mesh.from_pydata(data, [], faces)
mesh.update(calc_edges = True)

verts = obj.data.vertices

# Create a base shape key. This much seems to work.
bpy.context.scene.objects.active = obj
bpy.ops.object.shape_key_retime()
obj.data.shape_keys.use_relative = False
skeys = obj.data.shape_keys
skbs = skeys.key_blocks
skbs[obj.active_shape_key_index].interpolation = 'KEY_LINEAR'

# Create 10 sequential deformations
for n in range(10):
# Create new shape key
skbs[obj.active_shape_key_index].interpolation = 'KEY_LINEAR'
bpy.ops.object.shape_key_move(type='BOTTOM')
bpy.ops.object.mode_set(mode='EDIT')

# Deform the mesh
for i in range(len(verts)):
verts[i].co.z *= data[i][2]
mesh.update(calc_edges=True)
bpy.ops.object.mode_set(mode='OBJECT')


This does create the shape keys and put them in the correct order, but the mesh deformations aren't linked to the keys (because I'm modifying the vertices directly instead of using the edit-mode tools?), so the mesh always resembles the initial data. If I manually add another shape key after running this script, it magically takes the geometry of the last iteration, but the ones in between still don't seem to do anything.

What is the proper way to implement this (ideally without as much reliance on bpy.ops)?

You can create shapekeys directly as well as set the position of each vertex for each shapekey without using edit mode or even changing the active object. An object has a shape_key_add method that you should use instead of the operator, one advantage of the object method is that it returns the shapekey it creates, so you don't have to use an index into the array or set the active shapekey, you just work with each shapekey you create.

Each shapekey block has a data property that holds the location of each vertex in the object.

So after creating the mesh object -

verts = obj.data.vertices

sk_basis.interpolation = 'KEY_LINEAR'
obj.data.shape_keys.use_relative = False

# Create 10 sequential deformations
for n in range(10):
# Create new shape key

• Turns out adding from_mix=False to shape_key_add() does the trick. Oh, and I had to swap the "create shape key" and "deform mesh" blocks. – Zombie Feynman Jul 11 '18 at 13:47