Layout.prop()
supports bpy.props
and ID properties only. You can't use it for custom data layers such as bm.verts.layers
of the Bmesh module.
You would have to create a (temporary) set of properties for (selected) bm.verts
in order to display their custom data. Because there are no proper callbacks to detect changes to the mesh, the only way I see is to use brute force: re-fill a CollectionProperty
on every panel redraw with as many elements as there are (selected) vertices. These elements can be displayed without problems, but it's highly inefficient.
It would be much better to have a button the user needs to press to update the template_list()
with selected vertices, so that it doesn't happen all the time (i.e. 20x per second when moving the mouse over the panel).
import bpy
import bmesh
class MeshVertLayerList(bpy.types.UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
if self.layout_type in {'DEFAULT', 'COMPACT'}:
layout.label(item.name)
layout.prop(item, "value", text="", emboss=False)
elif self.layout_type in {'GRID'}:
layout.alignment = 'CENTER'
layout.label(text="")
class MESH_OT_vert_layer_add(bpy.types.Operator):
"""Tooltip"""
bl_label = "Add Vertex Layer"
bl_idname = "mesh.vert_layer_add"
@classmethod
def poll(cls, context):
return (context.object is not None and
context.object.type == 'MESH' and
context.object.mode == 'EDIT')
def execute(self, context):
bm = bmesh.from_edit_mesh(context.object.data)
try:
bm.verts.layers.float['test']
except KeyError:
bm.verts.layers.float.new('test')
else:
return {'CANCELLED'}
return {'FINISHED'}
class HelloWorldPanel(bpy.types.Panel):
"""Creates a Panel in the Object properties window"""
bl_label = "Hello World Panel"
bl_idname = "OBJECT_PT_hello"
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "data"
@classmethod
def poll(cls, context):
return (context.object is not None and
context.object.type == 'MESH' and
context.object.mode == 'EDIT')
def draw(self, context):
layout = self.layout
wm = context.window_manager
ob = context.object
me = ob.data
bm = bmesh.from_edit_mesh(me)
try:
l = bm.verts.layers.float['test']
except KeyError:
layout.label("No custom data layer 'test'.")
layout.operator("mesh.vert_layer_add")
return
wm.mesh_vert_layer.verts.clear()
for v in bm.verts:
if v.select:
item = wm.mesh_vert_layer.verts.add()
item.name = "Vertex #%i" % v.index
item.index = v.index
item.value = v[l]
layout.template_list("MeshVertLayerList", "", wm.mesh_vert_layer, "verts", wm.mesh_vert_layer, "index")
def upd(self, context):
try:
me = context.object.data
bm = bmesh.from_edit_mesh(me)
bm.verts[self.index][bm.verts.layers.float['test']] = self.value
bmesh.update_edit_mesh(me, tessface=False, destructive=False)
except:
pass
class MeshVertLayerVert(bpy.types.PropertyGroup):
value = bpy.props.FloatProperty(update=upd)
index = bpy.props.IntProperty()
class MeshVertLayer(bpy.types.PropertyGroup):
index = bpy.props.IntProperty()
verts = bpy.props.CollectionProperty(type=MeshVertLayerVert)
def register():
bpy.utils.register_module(__name__)
bpy.types.WindowManager.mesh_vert_layer = bpy.props.PointerProperty(type=MeshVertLayer)
def unregister():
bpy.utils.unregister_module(__name__)
del bpy.types.WindowManager.mesh_vert_layer
if __name__ == "__main__":
register()
As a side note: custom data "propagates" on mesh operations such as extrude. There's a patch in the making to disable it: https://developer.blender.org/T42865
edit: It's possible to bind an update operator to SELECTMOUSE
RELEASE
to update the CollectionProperty
after a selection change. It will work for right-clicks only however. I'm not sure if it's possible to do the same for modal selection operators such as lasso.
import bpy
import bmesh
class MeshVertLayerList(bpy.types.UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
if self.layout_type in {'DEFAULT', 'COMPACT'}:
layout.label(item.name)
layout.prop(item, "value", text="", emboss=False)
elif self.layout_type in {'GRID'}:
layout.alignment = 'CENTER'
layout.label(text="")
class MESH_OT_vert_prop_update(bpy.types.Operator):
"""Tooltip"""
bl_label = "Vertex Properties Update"
bl_idname = "mesh.vert_prop_update"
bl_options = {'INTERNAL'}
@classmethod
def poll(cls, context):
return (context.object is not None and
context.object.type == 'MESH' and
context.object.mode == 'EDIT')
def execute(self, context):
ob = context.object
me = ob.data
wm = context.window_manager
bm = bmesh.from_edit_mesh(me)
try:
l = bm.verts.layers.float['test']
except KeyError:
return {'CANCELLED'}
wm.mesh_vert_layer.verts.clear()
for v in bm.verts:
if v.select:
item = wm.mesh_vert_layer.verts.add()
item.name = "Vertex #%i" % v.index
item.index = v.index
item.value = v[l]
return {'FINISHED'}
class MESH_OT_vert_layer_add(bpy.types.Operator):
"""Tooltip"""
bl_label = "Add Vertex Layer"
bl_idname = "mesh.vert_layer_add"
@classmethod
def poll(cls, context):
return (context.object is not None and
context.object.type == 'MESH' and
context.object.mode == 'EDIT')
def execute(self, context):
bm = bmesh.from_edit_mesh(context.object.data)
try:
bm.verts.layers.float['test']
except KeyError:
bm.verts.layers.float.new('test')
else:
return {'CANCELLED'}
return {'FINISHED'}
class HelloWorldPanel(bpy.types.Panel):
"""Creates a Panel in the Object properties window"""
bl_label = "Hello World Panel"
bl_idname = "OBJECT_PT_hello"
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "data"
@classmethod
def poll(cls, context):
return (context.object is not None and
context.object.type == 'MESH' and
context.object.mode == 'EDIT')
def draw(self, context):
layout = self.layout
wm = context.window_manager
layout.template_list("MeshVertLayerList", "", wm.mesh_vert_layer, "verts", wm.mesh_vert_layer, "index")
def upd(self, context):
try:
me = context.object.data
bm = bmesh.from_edit_mesh(me)
bm.verts[self.index][bm.verts.layers.float['test']] = self.value
bmesh.update_edit_mesh(me, tessface=False, destructive=False)
except:
pass
class MeshVertLayerVert(bpy.types.PropertyGroup):
value = bpy.props.FloatProperty(update=upd)
index = bpy.props.IntProperty()
class MeshVertLayer(bpy.types.PropertyGroup):
index = bpy.props.IntProperty()
verts = bpy.props.CollectionProperty(type=MeshVertLayerVert)
addon_keymaps = []
def register():
bpy.utils.register_module(__name__)
bpy.types.WindowManager.mesh_vert_layer = bpy.props.PointerProperty(type=MeshVertLayer)
wm = bpy.context.window_manager
km = wm.keyconfigs.addon.keymaps.new(name='Mesh', space_type='EMPTY')
kmi = km.keymap_items.new(MESH_OT_vert_prop_update.bl_idname, 'SELECTMOUSE', 'RELEASE', any=True)
addon_keymaps.append((km, kmi))
def unregister():
bpy.utils.unregister_module(__name__)
del bpy.types.WindowManager.mesh_vert_layer
for km, kmi in addon_keymaps:
km.keymap_items.remove(kmi)
addon_keymaps.clear()
if __name__ == "__main__":
register()