16
$\begingroup$

What's the best/fastest way to construct a list of selected vertices for a mesh object in python?

I want to avoid iterating over every vertex in the mesh if possible, since that seems like a really inefficient/wasteful way to go about it.

There's a nice attribute called select_history in the bmesh module that gives you a list of vertices in the order that they were selected...but of course, it will only give you verts that have been selected manually (not by box or circle select) after the bmesh was initialized.

I've been searching through the edit mesh api for an attribute that will give me an updated list of selected verts (so that I don't have to iterate over the whole mesh). Does such a thing not exist?

$\endgroup$
18
$\begingroup$

You have to iterate over the entire mesh since blender isn't storing this data in a separate list internally.

You can use the following line of code to get a list if selected vertices from a bpy.types.Mesh object ('mesh' in the code):

selected_verts = list(filter(lambda v: v.select, mesh.vertices))

or with list comprehension:

selected_verts = [v for v in mesh.vertices if v.select]

However, in most cases, brute force looping through all the vertices of the mesh should be fine.

$\endgroup$
  • $\begingroup$ For this example I'd recommend using list comprehension, if you are going to use list(filter(...)), filter returns an iterator but if you only convert to a list immediately theres no reason to use it for simple checks like this. $\endgroup$ – ideasman42 Jun 28 '13 at 4:37
  • 1
    $\begingroup$ I'm in Edit Mode with just one vertex selected, but v.select is true for all the vertices in the mesh :( $\endgroup$ – endavid Mar 30 '16 at 11:13
  • 2
    $\begingroup$ It seems you have to switch back to Object mode so the selection gets "consolidated". I'll post an answer with the trick, just in case someone else gets stuck there. $\endgroup$ – endavid Mar 30 '16 at 11:30
  • $\begingroup$ @endavid Exactly! I think this is a bug rather than a feature. $\endgroup$ – Sibbs Gambling Jun 23 at 22:35
5
$\begingroup$

Just building on Gwenn's great answer,

  • If you are in Edit Mode, all the vertices may appear selected. Switch to Object Mode so the vertex selection gets updated.
  • You can directly access the vertices from active_object.data, without looking for the mesh in bpy.data.meshes

Code:

mode = bpy.context.active_object.mode
# we need to switch from Edit mode to Object mode so the selection gets updated
bpy.ops.object.mode_set(mode='OBJECT')
selectedVerts = [v for v in bpy.context.active_object.data.vertices if v.select]
for v in selectedVerts:
    print(v.co)
# back to whatever mode we were in
bpy.ops.object.mode_set(mode=mode)

Note that you can use the same logic to find selected edges,

selectedEdges = [e for e in bpy.context.active_object.data.edges if e.select]
$\endgroup$
  • $\begingroup$ This should be the accepted answer. $\endgroup$ – Prasanth Feb 7 at 15:09
1
$\begingroup$

problem concerning this subject, using blender v2.78

v.select value of v in bpy.context.active_object.data.vertices seems ALWAYS True:

  • I go in 3D View, select a mesh, go in edit mode: all vertices appear selected
  • I run below described script : all v.select values appear True
  • I manually do a deselect all vertices (pressing A in 3D View, Edit mode)
  • I run the script again : All v.select values remain True!

script does this:

for v in bpy.context.active_object.data.vertices:
    print(str(v.select))

More completely:

I'm using a custom operator to perform this, which assures Edit mode as needed state and allows a shortkey setup to call the operator :

class stitch(bpy.types.Operator):
    bl_idname = "my.printselectedstates"  
    bl_label = "printselectedstates"  

    @classmethod  
    def poll(cls, context):  
        ob = context.active_object  
        return ob is not None and ob.mode == 'EDIT' 

    def execute(self, context):  
        for v in context.active_object.data.vertices:
            print(str(v.index) + ' ' + str(v.select)) 
        return {'FINISHED'} 
$\endgroup$
  • $\begingroup$ solution : blenderartists.org/forum/archive/index.php/t-227726.html $\endgroup$ – user42646 Aug 25 '17 at 4:28
  • $\begingroup$ solution built in the code : inside that 'def execute' do a object/edit mode switch before the print loop. add these two lines bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.mode_set(mode='EDIT') $\endgroup$ – user42646 Aug 25 '17 at 4:28
1
$\begingroup$
import bpy
import numpy as np

ob = bpy.context.object
count = len(ob.data.vertices)
sel = np.zeros(count, dtype=np.bool)

ob.data.vertices.foreach_get('select', sel)

print(sel)

# super fast. No looping

The foreach_get and foreach_set methods are a fast way to get at certain collection properties in blender. They can be thousands of times faster than looping and hundreds of times faster than list comprehension. The code above returns a bool array. If you continue to work with numpy arrays in your code you can index subsets of your arrays with the bool array.

x = np.array([0, 0, 1, 0, 1], dtype=np.bool) 
y = np.array([1, 2, 3, 4, 5]) 
print(y[x]) 

>>> (numpy array([3, 4]))
$\endgroup$
  • $\begingroup$ the foreach_get and foreach_set methods are a fast way to get at certain collection properties in blender. They can be thousands of times faster than looping and hundreds of times faster than list comprehension. The code above returns a bool array. If you continue to work with numpy arrays in your code you can index subsets of your arrays with the bool array. x = np.array([0, 0, 1, 0, 1], dtype=np.bool) y = np.array([1, 2, 3, 4, 5]) print(y[x]) >>> (numpy array([3, 4])) $\endgroup$ – Rich Colburn Sep 1 '17 at 20:34
  • $\begingroup$ This is so fast! But what happens if the collection changes between the foreach_get and foreach_set operations? ie. bpy.context.scene.objects.foreach_get('select', s). Then two objects are deleted, or maybe added. bpy.context.scene.objects.foreach_set('select', s) will throw an array error. What might be a good way to handle situations like that? $\endgroup$ – andyV Jul 13 '18 at 21:03
  • 1
    $\begingroup$ If you were keeping track of selected objects and objects might be deleted between the time you create the list and the time you retrieve the list, you have other issues. In this case you could set up a class or dictionary for each object and have a select attribute for your class. You could check to see if the object associated with your class/dictionary is still in the scene. In most cases, I don't have object or vert counts changing between foreach_get and foreach_set in the code. $\endgroup$ – Rich Colburn Jul 14 '18 at 21:46

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.