Struggling with python scripting. How do you check if a face is selected?

Hi !

I struggle to understand how scripts are written with this 2.5x API
For example, I can’t find how to test if a face is selected in editmode or not.

I tried something like that:

active_object=bpy.context.active_object
active_mesh=active_object.data
active_mesh_faces=active_mesh.faces

for f in active_mesh_faces:
    if f.select==True:
        print (f)
        

But that doesn’t correspond to the effectively active faces.
Am I missing something here ?

You have to do it in object mode from what I have seen.

http://blenderartists.org/forum/archive/index.php/t-205592.html

So you have to jump from edit mode to object mode and back.

Your code looks correct.


import bpy

active_object=bpy.data.objects["Cube"]
active_mesh=active_object.data
active_mesh_faces=active_mesh.faces

for f in active_mesh_faces:
    if f.select==True:
        print (">")
        print (f)

It does look like that the mesh does not get updated until the user exits edit mode. So if you enter edit mode and select a face, then run your script, the selected faces returned are the faces that were selected prior to entering edit mode. One could call this a bug, but there may be another reason for this type of operation.

I guess you need a way to toggle the edit state before you begin scanning faces for selection.

Oh, that is interesting but a bit problematic !
What i wanted was to build a script that would constantly evaluate the total area of the selected faces in edit mode.
So it has to be updated permanently (just as edge lenght are permanently updated when you show them in edit mode)

Thanks both of you, that is quite surprising but it seems to work if i jump out of edit mode and right back in.

But i run into another problem. Here is my code:

import bpy

def getSurf():
    bpy.ops.object.mode_set(mode='OBJECT') # This is the trick mentionned on the thread: we go out of Edit mode and right back into it so that the faces show the correct boolean for face.select.
    bpy.ops.object.mode_set(mode='EDIT') # The reason is that face.select is evaluated once when entering edit mode. The value doesn't get refreshed when faces are selected or de-selected.
    
    area=0 #initialize our area
    faces=bpy.context.active_object.data.faces #We get the list of faces of the mesh
    for i in faces:
        if (i.select==True):
            area+=i.area        
    print('Area of the selected faces: '+ str(round(area,5))) #just some feed back in the console
    return (area)

class AreaPanel(bpy.types.Panel):
    bl_label = "Faces Area"
    bl_space_type = "VIEW_3D"
    bl_region_type = "UI"
    
    @classmethod  #So that this panel only appears when the object is a mesh and we're in edit mode
    def poll(self,context):
        if (context.mode=='EDIT_MESH' and context.object and context.object.type=='MESH'):
            return (True)
    
    def draw(self,context):
        layout=self.layout
        
        layout.label(str(getSurf()))

    
bpy.utils.register_class(AreaPanel)

If you define and rune the getSurf() function in the interactive pyton console, that is fine.
But if you Alt-P all the script so that you have the UI, you get the message that “Blender can’t switch mode while drawing/rendering”

I am pretty sure that what i tried here is crap !
All i need in fact is a single UI line that states the calculated area of the currently selected faces.

But as i struggle to understand what an operator is, how OOP works, and how this API is constructed, i don’t know where to start !




# ------ ------
bl_info = {
    'name': 'test',
    'author': '',
    'version': (0, 0, 0),
    'blender': (2, 5, 7),
    'api': 36812,
    'location': '',
    'description': '',
    'warning': '',
    'wiki_url': '',
    'tracker_url': '',
    'category': 'Mesh' }

# ------ ------
import bpy

class test_class():
    tmp = ''

def getSurf():

    ob_act = bpy.context.active_object
    bpy.ops.object.mode_set(mode = 'OBJECT')
    me = ob_act.data

    area = 0
    
    for f in me.faces:
        if f.select:
            area += f.area
    bpy.ops.object.mode_set(mode = 'EDIT')

    return area


class panel(bpy.types.Panel):

    bl_space_type = 'VIEW_3D'
    bl_region_type = 'TOOLS'

    bl_idname = 'p_0_id'
    
    bl_label = ''
    bl_context = 'mesh_edit'


    @classmethod  #So that this panel only appears when the object is a mesh and we're in edit mode
    def poll(self,context):
        if (context.mode=='EDIT_MESH' and context.object and context.object.type=='MESH'):
            return (True)




    def draw(self, context):
        layout = self.layout

        layout.label(text = test_class.tmp)
        layout.operator('mesh.op_0_id', text = 'Ok')
        

class operator(bpy.types.Operator):

    bl_idname = 'mesh.op_0_id'
    
    bl_label = ''
    bl_options = {'REGISTER', 'UNDO'}


    def execute(self, context):

        test_class.tmp = str(getSurf())
        
        
        return {'FINISHED'}




class_list = [ panel,
               operator ]




def register():
    for c in class_list:
        bpy.utils.register_class(c)


def unregister():
    for c in class_list:
        bpy.utils.unregister_class(c)


if __name__ == "__main__":
    register()


Thanks zmj100 !

I’ll have a good look at it and try to understand what you did.

In fact i wanted to avoid using a button: i thought it’d be much easier for the end-user if this was just some instant feedback.
I just tried and used that as a panel:

class AreaPanel(bpy.types.Panel):
    bl_label = "Faces Area"
    bl_space_type = "VIEW_3D"
    bl_region_type = "UI"
    
    @classmethod  #So that this panel only appears when the object is a mesh and we're in edit mode
    def poll(self,context):
        if (context.mode=='OBJECT' and context.object and context.object.type=='MESH'):
            return (True)
    
    def draw(self,context):
        layout=self.layout
        
        layout.label(str(context.object.location[0]))

It now appears in OBJECT mode and the text always displays the x location in real time. Something like that but with the area would be the best IMO.
Do you think it is possible in real-time ?
That “out and back into” edit mode thing may ruin the whole thing !



import bpy





ob_act = bpy.context.active_object
me = ob_act.data

area = [f.area for f in me.faces if f.select ]







class AreaPanel(bpy.types.Panel):
    bl_label = "Faces Area"
    bl_space_type = "VIEW_3D"
    bl_region_type = "UI"
    
    @classmethod  #So that this panel only appears when the object is a mesh and we're in edit mode
    def poll(self,context):
        if (context.mode=='OBJECT' and context.object and context.object.type=='MESH'):
            return (True)
    
    def draw(self,context):
        layout=self.layout
        
        layout.label(str(area[0]))
        
        
bpy.utils.register_class(AreaPanel)       
        


You have to go to edit mode to select face

Mmmh, it doesn’t seem to refresh properly when you select other faces…

BTW: don’t feel commited to do the work for me :o, i was just in need of someone to point me to the correction direction !

The more i think about it the more i am convinced that it WON’t be possible to display this sum of area in EDIT MODE in real time. I can’t see a mean to go round this “out and back in” loop for selected faces…
In which case I’d settle for a button, but that is a bis disappointing !

Let me try something else brb

How do you mean?


bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.object.mode_set(mode='OBJECT')

You would have to use something like a ‘context.area.tag_redraw()’ when the context changes everything is going to be updated including label in your panel.

@rarebit: yes but that isn’t allowed when included in the draw method of a panel class apparently (see post 5)

@zmj100: what do you mean with “when the context changes”? You mean when ANYTHING changes (number of selected faces for instance) ?
But the fact that the list of active faces is only evaluated once when entering edit mode seems to make the whole stuff impossible.
You cannot have blender toggle to OBJECT and back to EDIT every time you do a little something…

Another question, this time, going deeper into the algorithm in fact…

Imagine we have a default cube. With all selected faces, we should get 6x2x2=24square units. Fine. But if it has a scale factor ? Say, (1,2,1)
How are the faces now ? Well, this script doesn’t take care of that obviously, so it still returns “24”.
But what if we wanted to take it into account ? At first i thought "well let’s multiply by 1×2×1, that’s 2. But of course, this is plain stupid ! In that case we should get “40”, not “48” (the faces normal to the scaling direction keep the same value)

So i guess i’ll have to use face normals and compare them with the scaling Vector. In fact, a face that has a (0,0,1) normal should keep its area when the scaling is colinear (0,0,1), and have it multiplied when scaling is orthogonal (1,0,0) or (0,1,0)

No one has an idea concerning the last post ?

To sum it up:
I have the sum of the areas of the faces, now i want to take scale factor of the object in account. How ?

i think you need to control A the object then the values will be the right one for the faces or lenght ect…

happy 2.5

I don’t see what you mean RickyBlender. Can you develop your point ?

if you change object scale or loc size you need to Cltr-A the object so that the values in N panel will be the real ones!

unless you cannot do that like when uisng some Modifier!

so not certain what the whiole situation is for you right now!

do a test in viewport with an object
change scale for instance in objedt then in edit mode and check out values in n panel
then do a Ctrl-A on object and look at modifications in n Panel

can you give a complete script wiht the part your talking so we can test it !
would help to help you

happy 2.5

Oh yes, of course !
Sorry i didn’t understand your sentence, but now it is perfectly clear.

You are right of course, applying Scale works and the areas are fine after it. My question is in fact for the case where Scale is NOT applied. It would be nice if the script took care of any situation by taking that scale factor into account…

depends what is being done sometimes you need to apply scale and in other cases you don’t
so your sript have to take that into consideration if you need the faces values to be as expected

the script cannot guess for you if it is needed or not you have to program that in your algo
your the one taking this decision based on what you need and want to do!

hope it make sense now

happy 2.5