# handle vertex property changes with msgbus.subscribe_rna

I want to trigger a function when any mesh vertex is selected. After alot of research, using msgbus.subscribe_rna seems to be the way to go, unfortunately even though i can use it to trigger a function when say, objects are selected, when i try the same for changes in vertex propterties it has no effect.

import bpy

handle = object()

#subscribe_to = bpy.types.MeshVertex, "select" # Doesn't do anything
subscribe_to = bpy.context.object.data.vertices[0].path_resolve("select", False) #Doesn't do anything
#subscribe_to = bpy.types.LayerObjects, "active" # Triggers when an object is made active
#subscribe_to = bpy.types.Object, "location" # Triggers when an objects location is changed in N panel

def notify_test(*args):
print("Notify changed!", args)

bpy.msgbus.subscribe_rna(
key=subscribe_to,
owner=handle,
args=(),
notify=notify_test,
)

bpy.msgbus.publish_rna(key=subscribe_to)

• Interesting. Perhaps at issue here is akin to selecting vertices of Mesh.vertices has to be done in object mode. Running code above with MeshVertex.select triggers the message by running >>> C.object.data.vertices[0].select = False in console in object mode. – batFINGER Dec 14 '19 at 14:48
• Hmm that is interesting, presumably manually clicking the vertex to select it changes the .select attribute the same way as doing it through python so I can't imagine what the problem might be. – Thiedent Dec 14 '19 at 14:59
• The only other method i found was to generate an event for mouseClicks, which was extremely clunky and even so i would have to cycle through modes just to access any properties of the vertex. – Thiedent Dec 14 '19 at 15:01
• There is some "voodoo" seems like an instance is created to edit, then written back when mode toggled. Have done something similar by looking at edit bmesh select history active in a draw callback. see links in recent comment blender.stackexchange.com/questions/160877/… Make a dictionary of edit bmeshes from all meshes of objects in edit mode by mesh name, clear it if object mode changes. Works quite well. – batFINGER Dec 14 '19 at 15:06
• Hmm i'm not too familiar with that, but i'll look into it and see if i can make it work. Thanks – Thiedent Dec 14 '19 at 15:18

An alternative using bmesh and a draw callback

As mentioned in comments, here is an alternative using a draw callback on the 3d view.

Simply prints the active element.

import bpy
import blf
import bmesh

bmeshes = {}
#from gi.repository import Gdk
class DrawingClass:
def __init__(self, context, prop):
self.prop = prop
self.draw_text_callback,(context,),
'WINDOW', 'POST_PIXEL')

def draw_text_callback(self, context):
font_id = 0  # XXX, need to find out how best to get this.
# draw some text
blf.position(font_id, 15, 50, 20)
blf.size(font_id, 20, 72)

ob = context.object
meshes = set(o.data for o in context.selected_objects if o.mode == 'EDIT')
if context.mode == 'EDIT_MESH':
for m in meshes:
bmeshes.setdefault(m.name, bmesh.from_edit_mesh(m))
else:
bmeshes.clear()
return
me = ob.data
blf.draw(font_id, f"{bmeshes[me.name].select_history.active}")

def remove_handle(self):
bpy.types.SpaceView3D.draw_handler_remove(self.handle, 'WINDOW')

if __name__ == "__main__":
# test call
dc = DrawingClass(bpy.context, "Auto Layer Running......")
dns = bpy.app.driver_namespace
dns["dc"] = dc