2

I have a default cube ‘Cube’ which has 1 default material (active_material_index = 0). I’m adding another material dynamically via script that adds a Principled BSDF texture setup (CTRL+SHIFT+T) for this second material at active_material_index = 1 using bpy.ops.node.nw_add_textures_for_principled() as in the code below. The weird thing is that if i use active_material_index = 0 or any index for a material that already exists and have its Principled BSDF node already selected, the script actually works and properly adds the texture setup. But it doesn't work if you dynamically create the material and dynamically select the Principled BSDF node (white outline). It looks like something asynchronous is happening where the creation of the material is not ready yet when nw_add_textures_for_principled executes. Any idea? If the problem was the context, then how was it possible to work for the existing material?

enter image description here

import bpy

mat = bpy.data.materials.new(name='dynamically_created') mat.use_nodes = True

bpy.data.objects['Cube'].data.materials.append(mat) bpy.context.object.active_material_index = 1

bpy.context.area.ui_type = 'ShaderNodeTree' bpy.context.area.type = 'NODE_EDITOR'

m = bpy.context.object.active_material m.node_tree.nodes['Material Output'].select = False m.node_tree.nodes['Principled BSDF'].select = True m.node_tree.nodes.active = m.node_tree.nodes.get("Principled BSDF")

r = bpy.ops.node.nw_add_textures_for_principled(filepath="E:\ZZZZ\Projects\blender\textures\metal\Metal021_1K-JPG\", directory="E:\ZZZZ\Projects\blender\textures\metal\Metal021_1K-JPG\", files=[ {"name":"Metal021_1K_Color.jpg", "name":"Metal021_1K_Color.jpg"}, {"name":"Metal021_1K_Displacement.jpg", "name":"Metal021_1K_Displacement.jpg"}, {"name":"Metal021_1K_Metalness.jpg", "name":"Metal021_1K_Metalness.jpg"}, {"name":"Metal021_1K_NormalDX.jpg", "name":"Metal021_1K_NormalDX.jpg"}, {"name":"Metal021_1K_NormalGL.jpg", "name":"Metal021_1K_NormalGL.jpg"}, {"name":"Metal021_1K_Roughness.jpg", "name":"Metal021_1K_Roughness.jpg"}, {"name":"Metal021_PREVIEW.jpg", "name":"Metal021_PREVIEW.jpg"}], relative_path=True) print("result ==== ", r) #{'CANCELLED'}

UPDATE w/ Marty Fouts' Answer:

Thanks to Marty Fouts the context usage is properly implemented now but:

When I manually select the Principled BSDF node of material at index 0 (aka the already-existing material), the script does add the texture node setup but to material of index 0:

enter image description here

When NO Principled BSDF shader node is selected and i want the script to select this node from material index 1 (aka the dynamically generated material) it selects the node but unfortunately does NOT add the texture setup as I would have expected. The result is:

enter image description here

Hence the issue as described in the question title of this thread still persists

Here's the new complete script (make sure to use the default cube that already has 1 material on slot index 0):

import bpy

object = bpy.context.active_object material = bpy.data.materials.new(name='dynamically_created')

object.data.materials.append(material) object.active_material_index = len(object.material_slots) - 1

material.use_nodes = True tree = material.node_tree nodes = tree.nodes

material.node_tree.nodes['Material Output'].select = False material.node_tree.nodes['Principled BSDF'].select = True

material.node_tree.nodes.active = material.node_tree.nodes.get("Principled BSDF")

filepath="E:\ZZZZ\Projects\blender\textures\metal\Metal021_1K-JPG\" directory="E:\ZZZZ\Projects\blender\textures\metal\Metal021_1K-JPG\" files=[ {"name":"Metal021_1K-JPG.usda", "name":"Metal021_1K-JPG.usda"}, {"name":"Metal021_1K-JPG.usdc", "name":"Metal021_1K-JPG.usdc"}, {"name":"Metal021_1K_Color.jpg", "name":"Metal021_1K_Color.jpg"}, {"name":"Metal021_1K_Displacement.jpg", "name":"Metal021_1K_Displacement.jpg"}, {"name":"Metal021_1K_Metalness.jpg", "name":"Metal021_1K_Metalness.jpg"}, {"name":"Metal021_1K_NormalDX.jpg", "name":"Metal021_1K_NormalDX.jpg"}, {"name":"Metal021_1K_NormalGL.jpg", "name":"Metal021_1K_NormalGL.jpg"}, {"name":"Metal021_1K_Roughness.jpg", "name":"Metal021_1K_Roughness.jpg"}, {"name":"Metal021_PREVIEW.jpg", "name":"Metal021_PREVIEW.jpg"}, ] relative_path=True

#-----------------------------------------------------------------------------

Starting in 3.2 context overrides are deprecated in favor of temp_override

https://docs.blender.org/api/3.2/bpy.types.Context.html#bpy.types.Context.temp_override

They are scheduled to be removed in 3.3

def use_temp_override(): ''' Determine whether Blender is 3.2 or newer and requires the temp_override function, or is older and requires the context override dictionary ''' version = bpy.app.version major = version[0] minor = version[1] if major < 3 or (major == 3 and minor < 2): return False else: return True

win = bpy.context.window scr = win.screen areas = [area for area in scr.areas if area.type == 'NODE_EDITOR'] regions = [region for region in areas[0].regions if region.type == 'WINDOW']

if use_temp_override(): with bpy.context.temp_override(window=win, area=areas[0], region=regions[0], screen=scr): bpy.ops.node.nw_add_textures_for_principled( filepath=filepath, directory=directory, files=files, relative_path=relative_path ) else: override = { 'window': win, 'screen': scr, 'area': areas[0], 'region': regions[0], } bpy.ops.node.nw_add_textures_for_principled( override, filepath=filepath, directory=directory, files=files, relative_path=relative_path )

Harry McKenzie
  • 10,995
  • 8
  • 23
  • 51
  • If you check the console, you will see that it complains about no folder being selected Info: No Folder Selected. Do you want to set the folder programmatically or let the user pick (display the file browser)? – Robert Gützkow Jun 24 '22 at 07:11
  • hi. not in my case. no errors. i tried running it now by removing all materials and it runs without errors where console is saying Matched Textures etc and result {"FINISHED"} but no textures were added. I want to set it automatically. I have a list of folders through which i will iterate each folder to one material. – Harry McKenzie Jun 24 '22 at 07:17
  • it actually is inconsistent. sometimes its FINISHED sometimes CANCELLED. but it never adds the textures unless i run bpy.ops.node.nw_add_textures_for_principled() on an already created material. – Harry McKenzie Jun 24 '22 at 07:23
  • If you want to do it programmatically, you will have to provide the parameter for the directory https://developer.blender.org/diffusion/BA/browse/master/node_wrangler.py$3143 – Robert Gützkow Jun 24 '22 at 07:32
  • i did provide the parameters but i did not write the whole block in the snippet i provided. i just used ellipsis (for etc) as parameter with a comment saying that if ever someone was interested to test it they can generate this statement with their textures. I will update the post with the statement i used. – Harry McKenzie Jun 24 '22 at 07:44
  • @HarryMcKenzie There are two problems with your script. 1) You need to set the object's active_material_index to the slot of the new material. 2) You've misspelled regions as region in the then branch of the if statement. I'll update my answer to cover that. – Marty Fouts Jun 30 '22 at 17:35
  • hi @MartyFouts thanks for looking into it again. Unfortunately i still get CANCELLED with the "putting it all together" code and using my textures in "CHANGE ME" part and if you create a new file and leave everythig as is with default cube selected and the nodes in shader nodes selected, it will add the textures on the material with index 0. i need it to be added to the dynamically created material. can you show me a video of it working on your side including scrolling through the code? btw i updated my question with your complete code. – Harry McKenzie Jul 01 '22 at 03:19
  • The code in my answer, with my paths for textures works as described. 1) If you're still updating the material in slot 0, the code you're running isn't the code in your question, because the functions run on the active material slot, and the change sets that to the new material. 2) If you're getting cancelled you should also be getting an error message, either in the system console or the info window. What is it?
  • – Marty Fouts Jul 01 '22 at 03:35
  • @MartyFouts i copied your code again for a third time and edited the code in my question just to check the diff, and there is no diff. so it is certainly your code i ran and i still get the same result: Info: Select Principled BSDF result: {'CANCELLED'} – Harry McKenzie Jul 01 '22 at 04:03
  • Add your blend file to your question, please. I'll rename directories and textures; but you can see that the code works by having the Material Properties tab open and executing the first four lines. The active slot highlight changes. Are you at least seeing that? – Marty Fouts Jul 01 '22 at 04:08
  • Ah, I have found a problem. If I paste my script into the Python console it works but if I run it it doesn't. I think the scene needs to be updated after the active material slot is changed – Marty Fouts Jul 01 '22 at 04:13
  • 1
    @HarryMcKenzie I've confirmed that's the problem. If I run it interactively in the Python window then the node editor that is visible switches to the dynamic material and node wrangler operates on that material. But if you run it in the script, the switch isn't made until after the node wrangler routine runs. I'm out of time for today but maybe you can figure out how to make the window switch to the new material. – Marty Fouts Jul 01 '22 at 04:26
  • @MartyFouts yes that's exactly the weird issue I had as well when running it in console works perfectly but not in the Text Editor that's why I had to resort to this site to find the answer to this "asynchronous" problem, I wonder if it is even possible XD – Harry McKenzie Jul 01 '22 at 04:32
  • 1