Selected elements and bmesh

Can someone explain to me how I can reliably get the selected verts of a mesh with the bmesh module? It just seems totally unreliable to me.

So, say I have a new uv sphere in object mode, then in the console I do

#initiate the mesh object
import bmesh
tempMesh = bmesh.new()
tempMesh.from_mesh(bpy.context.active_object.data)

for v in tempMesh.verts:
    if ( v.select ):
        print(1)

Okay, so this prints “1”, 482 times (I didn’t really count, that’s how many verts it has though). So, when I go into edit mode and deselect all (which is the default), then select only one vert and do the same loop:

for v in tempMesh.verts:
    if ( v.select ):
        print(1)

It still prints “1”, 482 times. I can enter and exit edit mode repeatedly and it doesn’t update the selection state of the verts. Can someone kindly explain to me…HOW! can I make sure that when I ask a vert in a bmesh if it is selected, that it gives me a correct answer that accurately reflects what is selected in the viewport (in other words, what the user sees)?

Anyway, I have been reading about how you can create an invalid selection state, and that probably seems like it’s related to what’s happening to me. (read about this here:)

http://www.blender.org/documentation/blender_python_api_2_63_17/bmesh.html

I have been reading about flushing the selection state, but because the wording in the API is so vague, I really don’t even understand what that operation does.(select_flush(select) from the bmesh base type, under bmesh types in the api docs) I tried that and it does the same thing (still prints “1”, 482 times).

http://www.blender.org/documentation/blender_python_api_2_63_17/bmesh.types.html#bmesh.types.BMesh.select_flush

I am trying to build some tools, but it is frustrating for me because I mostly only have the API to go on and can’t even get vert data reliably. Would be a big help if someone could tell me what I’m doing wrong.

Edit:
Also, curiously, after doing all this, and then putting

tempMesh.from_mesh(bpy.context.active_object.data)

It also gives me an inaccurate vert and edge count when I simply print(tempMesh). It says that my sphere now has 964 total verts (I extruded a single edge a few times for fun before doing this also so I would expect maybe about a dozen more verts, instead it seems to have given me twice the vert count or something), and a total of 1984 edges and 1024 faces. And despite the fact that I had two verts selected when I types that loop in, it prints “1” many more than 2 times.

Hi, probaly you do not have a ‘late’ Blender version (which one?)


import bpy, bmesh
tempMesh = bmesh.new()
tempMesh.from_mesh(bpy.context.active_object.data)
print("===================
")
for v in tempMesh.verts:
    if ( v.select ):
        print(v.index)

Tested with the cube (and looking at selected indices via index visualizer!
I get e.g.

4
5
6
7

4

4

4
5
6
The visualizer shows correct selected indices only after leaving edit mode and going back as does the bmesh script above
the two time 4 a objectmode change was not done …

EDIT:
UV sphere behaves nice too

Blender 2.63 (sub 17)
build date: di 07-08-2012
build time: 20:34
build revision: 49656
build platform: Windows
build type: Release

I’m working on a stock 2.63a

Edit:
Also I started by loading factory settings to to avoid any other problems

Check it out, here’s another test in the console with stock 2.63a, with the factory blend file loaded (note that I start with the default cube and use that as the test object):

PYTHON INTERACTIVE CONSOLE 3.2 (r32:88445, Mar  2 2011, 16:59:21) [MSC v.1500 64 bit (AMD64)]

Command History:     Up/Down Arrow
Cursor:              Left/Right Home/End
Remove:              Backspace/Delete
Execute:             Enter
Autocomplete:        Ctrl+Space
Ctrl +/-  Wheel:     Zoom
Builtin Modules:     bpy, bpy.data, bpy.ops, bpy.props, bpy.types, bpy.context, bpy.utils, bgl, blf, mathutils
Convenience Imports: from mathutils import *; from math import *

>>> import bpy, bmesh
>>> tmp = bmesh.new()
>>> tmp.from_mesh(bpy.context.active_object.data)
>>> tmp
<BMesh(0x000000000D8E2EE8), totvert=8, totedge=12, totface=6, totloop=24>

>>> for v in tmp.verts:
...     if (v.select):
...         print(v)
... 
<BMVert(0x000000000D74BA68), index=0>
<BMVert(0x000000000D74BAA0), index=1>
<BMVert(0x000000000D74BAD8), index=2>
<BMVert(0x000000000D74BB10), index=3>
<BMVert(0x000000000D74BB48), index=4>
<BMVert(0x000000000D74BB80), index=5>
<BMVert(0x000000000D74BBB8), index=6>
<BMVert(0x000000000D74BBF0), index=7>
>>> # Now I switch to edit mode, unselect all, then select one vert and run the loop again, checing for selected verts
>>> for v in tmp.verts:
...     if (v.select):
...         print(v)
... 
<BMVert(0x000000000D74BA68), index=0>
<BMVert(0x000000000D74BAA0), index=1>
<BMVert(0x000000000D74BAD8), index=2>
<BMVert(0x000000000D74BB10), index=3>
<BMVert(0x000000000D74BB48), index=4>
<BMVert(0x000000000D74BB80), index=5>
<BMVert(0x000000000D74BBB8), index=6>
<BMVert(0x000000000D74BBF0), index=7>

>>> # Now I return to object mode, and run the loop again
>>> for v in tmp.verts:
...     if (v.select):
...         print(v)
... 
<BMVert(0x000000000D74BA68), index=0>
<BMVert(0x000000000D74BAA0), index=1>
<BMVert(0x000000000D74BAD8), index=2>
<BMVert(0x000000000D74BB10), index=3>
<BMVert(0x000000000D74BB48), index=4>
<BMVert(0x000000000D74BB80), index=5>
<BMVert(0x000000000D74BBB8), index=6>
<BMVert(0x000000000D74BBF0), index=7>

>>> # Now I enter and exit edit mode, then run the loop again.
>>> for v in tmp.verts:
...     if (v.select):
...         print(v)
... 
<BMVert(0x000000000D74BA68), index=0>
<BMVert(0x000000000D74BAA0), index=1>
<BMVert(0x000000000D74BAD8), index=2>
<BMVert(0x000000000D74BB10), index=3>
<BMVert(0x000000000D74BB48), index=4>
<BMVert(0x000000000D74BB80), index=5>
<BMVert(0x000000000D74BBB8), index=6>
<BMVert(0x000000000D74BBF0), index=7>

To me, it seems like the data is stale and that there’s no way to update it. Everything I’m working with is stock…

Update: Read the “stale data” section here…used bpy.types.Scene.update, which also does not seem to work…

http://www.blender.org/documentation/blender_python_api_2_63_17/info_gotcha.html

hmmm I think that there is this: one has to change each time the bmesh data see this code:



import bpy, bmesh
tmp = bmesh.new()
print("===============")
def dd():
    tmp.from_mesh(bpy.context.active_object.data) #PKHG needed???!!!!
    for v in tmp.verts:
        if (v.select):
            print(v)
            
dd()

with these tests all selected, nothing selected, one vertex selected

<BMVert(0x0B4EB278), index=0>
<BMVert(0x0B4EB2A4), index=1>
<BMVert(0x0B4EB2D0), index=2>
<BMVert(0x0B4EB2FC), index=3>
<BMVert(0x0B4EB328), index=4>
<BMVert(0x0B4EB354), index=5>
<BMVert(0x0B4EB380), index=6>
<BMVert(0x0B4EB3AC), index=7>

===============
<BMVert(0x0B5E30AC), index=5>

I think I read something about this behavior of bmesh …

Yes, remember that “a B-Mesh” (I say “a” because you can have as many as you want) is a snapshot of the mesh when you called


tempMesh.from_mesh(bpy.context.active_object.data)

When you modify the mesh in edit mode, the changes will not appear in your B-mesh!
Likewise, when you modify the B-mesh, the changes will not appear in the edit mesh!

The way to use B-mesh, is to first call


tempMesh.from_mesh(bpy.context.active_object.data)

then perform operations on the B-mesh, and finally call this to write the results back to the original edit mode mesh:


tempMesh.to_mesh(me)

Here’s the quote from the documentation:

When explicitly converting from mesh data, python owns the data, that is to say - that the mesh only exists while python holds a reference to it, and the script is responsible for putting it back into a mesh data-block when the edits are done.

Also, I don’t know exactly how the other command works:


bmesh.from_edit_mesh(mesh)

Perhaps that stays linked, but I doubt it. I think it’s just a convenience function, since you often want to grab the edit mode mesh.

One last thought, for anyone who thinks that B-mesh is required. You actually usually don’t need to use the B-Mesh API. For simple operations, it’s a lot easier to work directly with the regular mesh API. The B-mesh system is for when you need to do more complex, automated mesh operations (like advanced selection routines, remeshing, really anything that requires a good connectivity information for mesh traversal). Then it’s invaluable! But don’t use it unless you really need it.

Scene.update() does work, but it does not update your B-mesh. You have to explicitly call the “to_mesh” and “from_mesh” operators.

The formula that has been working for me is:
> be in editmode
> bm = from_edit_mesh(mesh)
> iterate verts/edges/faces and use the select_set(bool) method of each element
> finish with bm.select_flush_mode()

If you are only doing all selects or all deselects, then select_flush(bool) might work, but select_flush_mode handles cases like where if you set all four edges around a face to be selected, you would expect then that the face would be selected also.

EDIT: forgot to mention the viewport will still need refreshing
if called from buttons or having multiple views, something like this on your context


def v3d_refresh(ctx):
    for a in ctx.window.screen.areas:
        if a.type == 'VIEW_3D':
            for r in a.regions:
                r.tag_redraw()
            a.tag_redraw()