0
$\begingroup$

I wrote this simple python script for Blender 2.69, which creates a new operator. The operator is supposed to change the energy for four lamps in the scene (essentially switching them off and on again). However, while the script runs fine and creates the operator itself, when I select the operator, it has no effect on the lamps' energy.

Entire script:

import bpy


def main(context):
    for ob in context.scene.objects:
        print(ob)


class LightsOperator(bpy.types.Operator):

    bl_idname = "object.lights_operator"
    bl_label = "Headlight Operator"

@classmethod
def poll(cls, context):
    return context.active_object is not None

def execute(self, context):

    if bpy.data.objects['headlight1'].energy==0.0:
        bpy.data.objects['headlight1'].energy = 0.8
    else:
        bpy.data.objects['headlight1'].energy = 0.0


    if bpy.data.objects['headlight2'].energy==0.0:
        bpy.data.objects['headlight2'].energy = 0.8
    else:
        bpy.data.objects['headlight2'].energy = 0.0

    if bpy.data.objects['headlight3'].energy==0.0:
        bpy.data.objects['headlight3'].energy = 0.8
    else:
        bpy.data.objects['headlight3'].energy = 0.0

    if bpy.data.objects['headlight4'].energy==0.0:
        bpy.data.objects['headlight4'].energy = 0.8
    else:
        bpy.data.objects['headlight4'].energy = 0.0

def register():
    bpy.utils.register_class(LightsOperator)


def unregister():
    bpy.utils.unregister_class(LightsOperator)


if __name__ == "__main__":
    register()

    # test call
    bpy.ops.object.lights_operator()
$\endgroup$
1
  • $\begingroup$ Could it be that the poll and execute methods are not inside the class? Make sure that you indent them correctly $\endgroup$ Commented May 23, 2014 at 17:12

2 Answers 2

2
$\begingroup$

Here is a variation of the script that works on all lamp objects with their name (ob.data.name! ) starting with "headlight" (case insensitive):

import bpy

class LightsOperator(bpy.types.Operator):
    bl_idname = "object.lights_operator"
    bl_label = "Headlight Operator"

    def execute(self, context):

        for ob in context.scene.objects:
            if not ob.type == 'LAMP':
                continue
            lamp = ob.data
            if lamp.name.lower().startswith("headlight"):
                lamp.energy = 0.0 if lamp.energy >= 0.8 else 0.8

        return {'FINISHED'}

def register():
    bpy.utils.register_class(LightsOperator)

def unregister():
    bpy.utils.unregister_class(LightsOperator)

if __name__ == "__main__":
    register()

    # test call
    bpy.ops.object.lights_operator()

Note that it toggles the lights between 0.0 and 0.8, no matter what the initial value is. If you want to toggle only those with a specific energy, keep rounding errors in mind - it isn't really 0.8 unless you round(lamp.energy, 1):

>>> lamp.energy = 0.8

>>> lamp.energy == 0.8
False

>>> lamp.energy
0.800000011920929

>>> round(lamp.energy, 1)
0.8
$\endgroup$
0
$\begingroup$

Objects in scripting (bpy.data.objects) are not the same as the Objects you add with ShiftA.

Every scene holds a list (bpy_prop_collection) of objects. Now this is an abstract object that holds information that every object type (Mesh, Lamp, Armature) share - like position in worldspace, modifiers, etc. Here was your error.

Instead of calling bpy.data.objects[name].energy, the correct call is bpy.data.objects[name].data.energy.

Here is an improved version of the script that acts on the currently selected object (as the poll()-method suggests). Call it for every object by first setting context.scene.objects.active and then calling the operator with bpy.ops.object.lights_operator:

import bpy

def main(context):
    for ob in context.scene.objects:
        print(ob)

class LightsOperator(bpy.types.Operator):
    """Doc"""
    bl_idname = "object.lights_operator"
    bl_label = "Headlight Operator"

    # these two methods belong to the class, not global space
    @classmethod
    def poll(cls, context):
        # can also use context.object instead of context.active_object
        return context.object is not None

    def execute(self, context):
        # get the object
        obj = context.object

        # check if it is a lamp
        if not obj.type == 'LAMP':
            return {'CANCELLED'}

        # now this is an abstraction by Blender. Every scene
        # has objects. Those objects contain the data (mesh,
        # curve, and LAMPS). So to access the data of the
        # lamp we do
        lamp = obj.data

        if lamp.energy == 0:
            lamp.energy = 0.8
        else:
            lamp.energy = 0

        return {'FINISHED'}

def register():
    bpy.utils.register_class(LightsOperator)

def unregister():
    bpy.utils.unregister_class(LightsOperator)

if __name__ == "__main__":
    register()

    # test call
    bpy.ops.object.lights_operator()
$\endgroup$
6
  • $\begingroup$ The original indentation was just a result of copy+pasting. But to just make sure, I copy+pasted your indented script, and it just returns a "Python script fail". Typing directly into the console gives me a whole bunch of "unexpected indent"s. $\endgroup$
    – Ollie
    Commented May 23, 2014 at 17:28
  • $\begingroup$ It may give a "Python script fail" if the following objects are not in the current scene: 'headlight1', 'headlight2', 'headlight3', 'headlight4'. Check if there are four lamps with exactly thos names (yes capitalization matters). I know that it gives an error when run in the console. I'll update so that you can run it from console. $\endgroup$ Commented May 23, 2014 at 17:34
  • $\begingroup$ Yep those exact names are definitely there :) $\endgroup$
    – Ollie
    Commented May 23, 2014 at 17:38
  • $\begingroup$ Okay the script is updated and should now work as described above $\endgroup$ Commented May 23, 2014 at 18:03
  • $\begingroup$ Thanks for your help but the new script still doesn't work. :( $\endgroup$
    – Ollie
    Commented May 24, 2014 at 10:08

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .