6
$\begingroup$

I have a script which adds a material to an object. The script is using bpy.ops.object.material_slot_add(). However, this only works when the script is used from the text editor. It has polling/context errors when used differently.

What Blender 2.75a API commands provide the low level equivalent to bpy.ops.object.material_slot_add()?

$\endgroup$
  • $\begingroup$ I use this operator often from the 3D view and console. It is not limited to the text editor. It also works in object mode, edit mode, sculpt mode, weight paint, texture paint... Where are you running the script from? You might have a panel in some specific screen that might be out of context, but you can probably override this context and run the operator anyway (blender.stackexchange.com/search?q=context+override) $\endgroup$ – TLousky Oct 12 '15 at 13:21
  • $\begingroup$ The command will be used in a function which will be passed to the render handler: bpy.app.handlers.render_pre.append() . Because of challenges in getting the override set up properly and a desire to make create scripts which operate independent of the context, I'm trying to convert all bpy.ops to low level equivalents. $\endgroup$ – Ed Tate Oct 12 '15 at 13:26
  • $\begingroup$ I don't think you have another way to add a material slot via python. I think I tried finding one before. Maybe some other person here knows something I don't. Anyway, try the solution in the answer below. Small trivial thing, but sometimes those are the hardest to debug. $\endgroup$ – TLousky Oct 12 '15 at 13:28
6
$\begingroup$

Example code

Adds two material slots, m1 (if there is already an active material slot it will replace it with m1) and m2

import bpy

mat1 = bpy.data.materials.new("m1")
mat2 = bpy.data.materials.new("m2")

ob = bpy.context.object

ob.active_material = mat1
ob.data.materials.append(mat2)

An empty slot can be added with

ob.data.materials.append(None)

Hence can add either data or object materials this way by setting the slot link.

import bpy

materials = (
    (bpy.data.materials.new("RED"), 'DATA'),
    (bpy.data.materials.new("BLUE"), 'OBJECT'),
    )

ob = bpy.context.object
me = ob.data

for m, link in materials:

    # add a material adds a slot
    print(m.name)
    me.materials.append(None)

    slot = ob.material_slots[-1]
    slot.link = link
    slot.material = m

Thankyou to @Lambdadelta for pointing out

enter image description here Note if "Link Materials to" set to "Object" instead of default "Object Data" in the blender preferences, then ob.data.materials.append(None) adds a slot to all linked objects, whereas bpy.ops.object.material_slot_add() adds only to the active object

| improve this answer | |
$\endgroup$
  • 1
    $\begingroup$ Note if "Link Materials to" set to "Object" instead of default "Object Data" in the blender preferences, then ob.data.materials.append(None) adds a slot to all linked objects, whereas bpy.ops.object.material_slot_add() adds only to the active object $\endgroup$ – Lambdadelta Mar 28 at 4:36
  • $\begingroup$ @Lambdadelta cheers Added as edit.. Tricky to get around as materials.clear() as doesmaterials.pop(index=index) $\endgroup$ – batFINGER Mar 28 at 12:08
0
$\begingroup$

The only case I can think of where this operator will not work is that the object you want to add a material slot to is not selected and active. Make sure of both in your script before calling the operator:

myObj.select = True
bpy.context.scene.objects.active = myObj
| improve this answer | |
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.