22
$\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$
21
$\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$
6
  • $\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
  • 2
    $\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
  • 3
    $\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
  • 1
    $\begingroup$ This doesn't work for multi-object editing in 2.8. Any idea how to solve it? $\endgroup$ Jan 1 '20 at 20:07
  • 1
    $\begingroup$ update: I found a solution. See my answer down this page. $\endgroup$ Jan 2 '20 at 0:18
10
$\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$
1
  • 1
    $\begingroup$ This should be the accepted answer. $\endgroup$
    – Prasanth
    Feb 7 '19 at 15:09
7
$\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$
3
  • $\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$ 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
  • 2
    $\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$ Jul 14 '18 at 21:46
3
$\begingroup$

Since Blender 2.8, multi-object editing makes the previously given answers insufficient, as they don't list the selected vertices of non active objects.

The following snippet solves this issue by creating a dictionary with the paths of the edited objects as keys and the indices of their respective selected vertices as key values :

import bpy
import bmesh

dic_v = {}
for o in bpy.context.objects_in_mode:
    dic_v.update( {o : []} )
    bm = bmesh.from_edit_mesh(o.data)
    for v in bm.verts:
        if v.select:
            dic_v[o].append(v.index)

print(dic_v)
$\endgroup$
1
3
$\begingroup$

Related to the numpy approach which is super fast to get a count - the issue for me was - now that I have a count - how do I quickly get which vertex it is without loops:

    vert_count = len(obj.data.vertices)
    sel = np.zeros(vert_count, dtype=np.bool)
    obj.data.vertices.foreach_get('select', sel)
    sel_count = np.count_nonzero(sel)

    # get the actual vertices that are selected
    nv = np.array(obj.data.vertices)
    sel_vert_list = nv[sel]
$\endgroup$
4
  • 1
    $\begingroup$ Is this an answer or a question? $\endgroup$ May 10 '20 at 10:59
  • 3
    $\begingroup$ It is my solution that adds to the OP the code to get the actual list of selected vertices. $\endgroup$
    – EMCS
    May 11 '20 at 13:27
  • $\begingroup$ I did some print to understand better. very interesting. is there a kind of limit of vertices number from where this is better to use numpy? I mean for 50 vertices if you do [v for v in obj.data.vertices if v.select] is numpy slower? or not really and this is useless to do a condition to change the script? $\endgroup$ Oct 13 at 18:31
  • $\begingroup$ well I found my answer as fast I asked blog.michelanders.nl/2016/02/… $\endgroup$ Oct 13 at 18:38
3
$\begingroup$

In newer versions of Blender you can use (while in Edit Mode):

bpy.context.active_object.data.total_vert_sel
bpy.context.active_object.data.total_edge_sel
bpy.context.active_object.data.total_face_sel

See: https://docs.blender.org/api/current/bpy.types.Mesh.html?highlight=_sel#bpy.types.Mesh.total_edge_sel

Also count_selected_items() returns the number of selected items (vert, edge, face):

bpy.context.active_object.data.count_selected_items()

To programmatically switch to Edit Mode call: bpy.ops.object.mode_set(mode="EDIT”)

$\endgroup$
2
  • $\begingroup$ Thanks @brockmann .. great tip/edit. $\endgroup$ Feb 22 at 22:14
  • $\begingroup$ Simple and super useful. Great answer overall! $\endgroup$ Sep 24 at 17:45
3
$\begingroup$

Edit mode bmesh & numpy

Thought I'd throw this one in the ring as an example to speed up getting selection purely from an edit mode bmesh.

For example sake, will take a test run via entering code in python console. The default cube "Cube" is active object.

Load in an edit mode bmesh from the mesh of the current edit object.

>>> import bmesh    
>>> bm = bmesh.from_edit_mesh(C.edit_object.data)

Create a numpy verts array

>>> import numpy as np
>>> verts = np.array(bm.verts)
>>> verts
array([<BMVert(0x7f8ac738e090), index=0>,
       <BMVert(0x7f8ac738e0c8), index=1>,
       <BMVert(0x7f8ac738e100), index=2>,
       <BMVert(0x7f8ac738e138), index=3>,
       <BMVert(0x7f8ac738e170), index=4>,
       <BMVert(0x7f8ac738e1a8), index=5>,
       <BMVert(0x7f8ac738e1e0), index=6>,
       <BMVert(0x7f8ac738e218), index=7>], dtype=object)

Define a "foreach get" method

Example of np.frompyfunc as suggested in this stackoverflow answer

frompyfunc iterates as the [a for a in A.flat] does, but is somewhat faster, and throws all the power of numpy broadcasting at the task.

>>> selected = np.frompyfunc(lambda a: a.select, 1, 1)

With all verts selected

>>> selected(verts)
array([True, True, True, True, True, True, True, True], dtype=object)

Change selection in UI to single vert, run again

>>> selected(verts)
array([False, False, False, False, False, True, False, False],
      dtype=object)

Cannot index with the object dtype

>>> sel = selected(verts).astype(bool)
>>> sel
array([False, False, False, False, False,  True, False, False])

>>> verts[sel]
array([<BMVert(0x7f8ac738e1a8), index=5>], dtype=object)

Above could be used to create a function, "get_where" that takes the sequence, the property name and value to check, and the property to return

selected_verts = get_where(bm.verts, "select", True, "index")

returning an array of indices of selected vertices.

$\endgroup$
2
  • 1
    $\begingroup$ Thanks a ton, I was exactly looking for functional examples like this. frompyfunc & get_where are so great. $\endgroup$
    – melMass
    Aug 14 at 21:53
  • $\begingroup$ Thanks, appreciated. $\endgroup$
    – batFINGER
    Aug 15 at 6:44
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$
2
  • $\begingroup$ solution : blenderartists.org/forum/archive/index.php/t-227726.html $\endgroup$
    – user42646
    Aug 25 '17 at 4:28
  • 1
    $\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
0
$\begingroup$

print index of selected vertex with order

import bpy
import bmesh

obj = bpy.context.object

data = obj.data
bm = bmesh.from_edit_mesh(data)

v = [s.index for s in bm.select_history if isinstance(s, bmesh.types.BMVert)]
print(v)
$\endgroup$
1
  • $\begingroup$ As mentioned in question, select history only gives manually selected elements, not those selected by other means. $\endgroup$
    – batFINGER
    Mar 9 '20 at 11:50

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.