Come verificare se la proprietà del nodo non è None durante l'iterazione dei materiali?

Aug 17 2020

Sto riscontrando un errore senza attributi durante l'iterazione di nodi e gruppi di nodi in alcuni file blend.

Errore: AttributeError: l'oggetto 'NoneType' non ha l'attributo 'nome'

Come posso verificare se gli oggetti non hanno proprietà 'name' e saltarli?

   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':

Risposte

3 batFINGER Aug 17 2020 at 17:41

Un attributo può essereNone

L'errore NoneType has no attributeè un errore comune riscontrato negli script di Blender. Ad esempio può essere il caso checontext.object is None

All'OP consiglierei di modificare il titolo della domanda per riflettere maggiormente questo messaggio di errore.

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

lanciato da node.node_tree.name == 'NodeGroupName'sta informando node.node_treeha il valore None. Può verificare che sia così, ad es

if node.node_tree is  None:

al contrario di

if hasattr(node, "node_tree"):

Entrambi saranno veri quando lo è l'albero dei nodiNone

In realtà è strano che il codice in questione generi questo errore, poiché non sono (ancora) a conoscenza di come aggiungere un nodo di gruppo e non avere node_tree collegato. Ipotizzare che questo sia il risultato dell'eliminazione di un gruppo di nodi da bpy.data.node_groupsquello che era un nodo di un albero di nodi materiali..

quindi l'ultima riga del copione della domanda potrebbe essere

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

poiché il concatenamento e cessa al primoFalse

Usandogetattr

Dal momento che praticamente tratteremmo lo stesso usando l'attributo get con il terzo argomento predefinito

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

sarà Nonein entrambi i casi. IMO questo "trionfa" utilizzando sia l'albero dei nodi has che il test degli attributi del nome. Nonenon ha nulla, se sappiamo che non c'è nessun ulteriore test per gli attributi sembrerebbe superfluo Può anche garantire che se un nodo albero non Nonelo è avrà un nome .bpy.types.ShaderNodeTree.name

Sceneggiatura di prova

Usando quanto sopra con il "doppio test" per un nodo di 'GROUP'tipo verifica che abbia un albero di nodi non nessuno.

Crea un dizionario, con chiavi corrispondenti al nome del gruppo di nodi contenente un elenco di nome materiale, tupples nome nodo.

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) 

Produzione.

File con 1 gruppo di nodi "Test_Material", 2 materiali "TestMat" e "TestMat.001".

Il gruppo di nodi viene utilizzato in entrambi i materiali con il nome "Gruppo" in entrambi.

['Test_Material']

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

Appunti:

Attenzione all'equivalenza Trueo Falsenei test. Per impostazione predefinita, Python equipara la maggior parte dei tipi a True o false. Alcuni esempi di false sono None, il numero intero 0e una lista vuota[]

Metodi come hasattrreturn o TrueoFalse

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

se foo.baresiste questo si legge in qualche modo simile a

if True == True:

invece usa semplicemente

if hasattr(foo, "bar"):

Cambiare leggermente sopra per restituire boolean false invece di none quando il nodo non ha l'attributo node_tree, if nt:sarà falso in entrambi i casi, ma possiamo discernere tra i due.

        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

Basato sul commento di 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':