Comment vérifier si la propriété du nœud n'est pas None lors de l'itération des matériaux ?

Aug 17 2020

Je rencontre une erreur d'absence d'attribut lors de l'itération des nœuds et des groupes de nœuds dans certains fichiers blend.

Erreur : AttributeError : l'objet 'NoneType' n'a pas d'attribut 'name'

Comment vérifier si les objets n'ont pas de propriété 'name' et l'ignorer?

   for mat in bpy.data.materials:
        if mat.use_nodes == True:
            for node in mat.node_tree.nodes:
                #check if no has attribute name
                if node.type in ["GROUP"] and node.node_tree.name == 'NodeGroupName':

Réponses

3 batFINGER Aug 17 2020 at 17:41

Un attribut peut êtreNone

L'erreur NoneType has no attributeest une erreur courante rencontrée dans les scripts Blender. Par exemple, il peut arriver quecontext.object is None

Pour l'OP, je recommanderais de modifier le titre de la question pour refléter davantage ce message d'erreur.

Error: AttributeError: 'NoneType' object has no attribute 'name'

jeté par node.node_tree.name == 'NodeGroupName'est informant node.node_treea la valeur None. Peut tester si c'est le cas, c'est-à- dire

if node.node_tree is  None:

par opposition à

if hasattr(node, "node_tree"):

Les deux seront vrais lorsque l'arborescence de nœuds estNone

C'est en fait un cas étrange que le code en question génère cette erreur, car je ne sais pas (encore) comment ajouter un nœud de groupe et n'avoir aucun node_tree lié. Supposons que cela soit le résultat de la suppression d'un groupe de nœuds bpy.data.node_groupsqui était un nœud d'une arborescence de nœuds de matériau.

par conséquent, la dernière ligne du script de question pourrait être

if (node.type in ["GROUP"]  
        and node.node_tree 
        and node.node_tree.name == 'NodeGroupName'
        ):

puisque l'enchaînement des ands cesse le premierFalse

Utilisantgetattr

Puisque nous traiterions à peu près la même chose en utilisant l'attribut get avec le troisième argument default

node_tree = getattr(node, "node_tree", None)

le sera Nonedans les deux cas. IMO cela "l'emporte" en utilisant à la fois l'arborescence de nœuds puis le test d'attribut de nom. Nonen'a rien, si nous savons qu'il n'y en a pas, tester davantage les attributs semblerait superflu . Peut également garantir que si un arbre de nœuds ne l'est pas None, il aura un nom .bpy.types.ShaderNodeTree.name

Scénario de test

En utilisant ce qui précède avec le "double test" pour un nœud de 'GROUP'type check, il a une arborescence de nœuds non nulle.

Créez un dictionnaire, avec des clés correspondant au nom du groupe de nœuds contenant une liste de noms de matériaux et de noms de nœuds.

import bpy
from bpy import data
from collections import defaultdict

# all the group nodes in blendfile
print(data.node_groups.keys())
    
group_nodes = defaultdict(list)
# create a look up dictionary 
for m in data.materials:
    if m.use_nodes:
        for n in m.node_tree.nodes:
            nt = getattr(n, "node_tree", None)
            if nt and n.type == 'GROUP':
                group_nodes[nt.name].append((m.name, n.name))
                
print(group_nodes) 

Production.

Fichier avec 1 groupe de nœuds "Test_Material", 2 matériaux "TestMat" et "TestMat.001".

Le groupe de nœuds est utilisé dans les deux matériaux avec le nom "Groupe" dans les deux.

['Test_Material']

defaultdict(<class 'list'>, {'Test_Material': [('TestMat', 'Group'), ('TestMat.001', 'Group')]})

Remarques:

Méfiez-vous des assimilation à Trueou Falsedans les tests. Par défaut, Python assimile la plupart des types à True ou False. Quelques exemples de false sont None, l'entier 0et une liste vide[]

Des méthodes comme hasattrreturn soit TrueouFalse

if hasattr(foo, "bar") == True:

s'il foo.barexiste, cela ressemble un peu à

if True == True:

utilisez plutôt simplement

if hasattr(foo, "bar"):

Changer légèrement ci-dessus pour renvoyer boolean false au lieu de none lorsque le nœud n'a pas d'attribut node_tree, if nt:sera faux dans les deux cas, mais nous pouvons discerner entre les deux.

        nt = getattr(n, "node_tree", False)
        if nt and n.type == 'GROUP':
            group_nodes[nt.name].append((m.name, n.name))
        if nt is None:
            print(f"{m.name}:{n.name} node_tree is None")
0451 Aug 17 2020 at 04:14

Basé sur le commentaire de Ron Jensens :

    for mat in bpy.data.materials:
        if mat.use_nodes == True:
            for node in mat.node_tree.nodes:
                if hasattr(node, 'node_tree') == True:
                    if hasattr(node.node_tree, 'name') == True:
                        if node.type in ["GROUP"] and node.node_tree.name == 'NodeGroupName':