1
$\begingroup$

I try to get the active object by "bpy.context.scene.objects.active" in my operator, but I receive a error 'Context' object has no attribute 'objects', what's wrong?My scripts is as follow:

import bpy                                                               
from bpy.props import *                         

cube=bpy.data.objects["Cube"]                                           

class HelloError(bpy.types.Operator):
    bl_idname = "object.hello_error"
    bl_label = "HelloError"
    bl_options={'REGISTER','UNDO'}

    my_property= BoolProperty(name="my_property", default=True)
    def execute(self, context):
        print("--------------------------------")
        if self.my_property:
            print("do something......")

        #set the active object as a existed cube
        bpy.context.scene.objects.active =cube
        print("objects:",bpy.context.scene.objects)
        print("cur_active:",bpy.context.scene.objects.active.name)
        return {'FINISHED'}                                                   
def register():
    bpy.utils.register_class(HelloError)                                  
def unregister():                      
    bpy.utils.unregister_class(HelloError)                                 
if __name__ == "__main__":
    register()

enter image description here enter image description here enter image description here enter image description here And the blender The file will be uploaded soon(It seems that the website"https://blend-exchange.giantcowfilms.com/"cannot be accessed now in my computer, i will upload the file once it is accessible)

$\endgroup$

1 Answer 1

2
$\begingroup$

You need to be aware that Blender, when changing such custom properties, internally runs an undo step, and then re-executes the script. The trouble with your script is that you define the content of the variable cube right in the __main__ area of your script. This means it is only run once when the script registers. When the undo->redo happens, this can get lost. In fact, when I just copy-paste your code into a new file, run the script, run the operator and toggle the property, the pasted script disappears (pasting it is also undone in the undo step).

To fix it, get the cube while executing. It could also make sense to get it in the invoke() function, but let's keep it simple for now. So try this modified script instead:

import bpy                                                               
from bpy.props import BoolProperty                        


class HelloError(bpy.types.Operator):
    bl_idname = "object.hello_error"
    bl_label = "HelloError"
    bl_options={'REGISTER','UNDO'}

    my_property= BoolProperty(name="my_property", default=True)
    def execute(self, context):
        print("--------------------------------")
        if self.my_property:
            print("do something......")

        # get desired object within execute() function instead!!
        # also, retrieve it from the current scene objects,
        # this ensures it exists within the scene and can be set active
        cube = context.scene.objects.get("Cube")                                         

        #set the active object as a existed cube
        context.scene.objects.active =cube
        print("objects:", context.scene.objects)
        print("cur_active:", context.scene.objects.active.name)
        return {'FINISHED'}                                                   

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

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

if __name__ == "__main__":
    register()

I've done a few other style modifications to it, namely using the context variable that is passed into the execute() method instead of constantly grabbing it from bpy.context, and using context.blend_data instead of bpy.data (a call which I removed later on, see next paragraph).

Finally, batFINGER pointed out in a comment that it is recommended to pull the cube object from context.scene.objects instead of context.blend_data.objects, as this will make sure the cube is already linked to the scene, and later on can be set as active object. Using the get() method, the variable will be set to None if there is no object named Cube, instead of throwing an error immediately. You could later on use a sanity check like:

        if not cube:
            return{'CANCELLED'}
$\endgroup$
4
  • $\begingroup$ Recommend cube = context.scene.objects.get("Cube") to ensure it is linked to the scene. (or link it if it isn't) $\endgroup$
    – batFINGER
    Commented Dec 9, 2018 at 3:46
  • $\begingroup$ @batFINGER thanks for pointing this out, I've updated the answer accordingly. $\endgroup$
    – aliasguru
    Commented Dec 9, 2018 at 8:40
  • $\begingroup$ @aliasguru Just a question as I am not a programmer but wouldn't context.active_object = cube be more effective as it is compatible in blender 2.79 and 2.8 which does not appear to translate scene.objects.active? $\endgroup$
    – Ratt
    Commented Dec 9, 2018 at 10:44
  • $\begingroup$ @Ratt Both context.active_object as well as the shorter form context.object are read-only properties, which cannot be set. So traversing through context.scene.objects.active in 2.79 and context.view_layer.objects.active in 2.80 is the feasible way at the moment. $\endgroup$
    – aliasguru
    Commented Dec 9, 2018 at 11:25

You must log in to answer this question.

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