5
$\begingroup$

Currently running 2.79

I'm having issues figuring out how to select objects: This is my code, what I want it to do is create two spheres, and then resize the first created sphere after the second is created

def sphere(x, y, z, radius, resolution=30):
    bpy.ops.mesh.primitive_uv_sphere_add(segments=resolution, ring_count=resolution, view_align=False, enter_editmode=False, location=(x, y, z))
    bpy.ops.transform.resize(value=(radius, radius, radius), constraint_axis=(False, False, False), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1)
def renameActive(name):
   bpy.context.active_object.name = name
def scale(x, y, z):
    bpy.ops.transform.resize(value=(xScale, yScale, zScale), constraint_axis=(False, False, False), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1)
#change active
sphere(3,0,0,1)
renameActive("RescaleLater")
sphere(0,0,0,1)
renameActive("leaveAlone")
scene = bpy.context.scene
objs = scene.objects
rescaleSphere = objs['RescaleLater']
scene.objects.active= rescaleSphere
print(objs.active)
scale(1,2,1)

After running this, console shows:

< bpy_struct, Object("RescaleLater")>

So the active object should be what is being rescaled, but as shown below the sphere titled leaveAlone is being scaled.

This is what shows up; the leaveAlone sphere has been resized but in the scene outliner RescaleLater has its name highlighted, though it's not actually 'active' in the sense where I can edit it through scripting as the activated object.

Screenshot showing LeaveAlone and RescaleLater

More elaboration: I know I can just resize immediately after creating the first sphere, but I need to figure this out to implement applying Boolean modifiers on specific objects (I can create the Boolean modifiers and tie them to the correct objects but haven't been able to apply), my code is more complex but this I've simplified it to this to get to the core problem of not being able to manually set the active object through python scripting. I can provide full code if helpful. Have also tried all the other versions of this question that I've seen here/elsewhere but haven't found something that works.

$\endgroup$

2 Answers 2

4
$\begingroup$

You could return the sphere object from createSphere so you can access it directly later:

def sphere(x, y, z, radius, resolution=30):
    bpy.ops.mesh.primitive_uv_sphere_add(segments=resolution, ring_count=resolution, view_align=False, enter_editmode=False, location=(x, y, z))
    bpy.ops.transform.resize(value=(radius, radius, radius), constraint_axis=(False, False, False), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1)
    return bpy.context.active_object

Now the sphere creation, renaming and scaling could look as follows:

first_sphere = sphere(3,0,0,1)
first_sphere.name = "RescaleLater"
second_sphere = sphere(0,0,0,1)
second_sphere.name = "leaveAlone"
# Rescale first sphere
first_sphere.scale = (1, 2, 1)

Another way to retrieve and object: from bpy.data.objects, if you know the name of the object

obj = bpy.data.objects.get("ObjectName")
if obj:
    obj.scale = (1, 2, 1)
$\endgroup$
1
  • $\begingroup$ Thank you, I didn't realize i could call bpy.ops.transform functions directly on to objects. Is there a way that i would be able to do something similar with the bpy.ops.object.modifier_apply(apply_as='DATA', modifier="nameofmodifier")? Ive tried and first_sphere.modifier_apple(etc...) calls an error " 'Object' object has no attribute 'modifier_apply' " $\endgroup$
    – T. Mat
    Apr 29, 2019 at 17:47
1
$\begingroup$

Use the object reference

One issue here is that operators often work on the selected objects. When a primitive is created, it is both active and selected. In your code above if the other sphere, albeit not active, is selected, it will be scaled.

If we have some object reference ob and we want it to be the only object selected and active before calling an operator

for o in context.selected_objects:
    o.select = False
ob.select = True
scene.objects.active = ob
bpy.ops.foo.bar(...)

I would seriously reconsider the logic of context reliant methods and pass and or return object references. As shown in sphere method in code below, can return the reference to the newly operator created object via the context.

Can rename the object via the reference. Because of blenders need to have unique object names per blend. eg in default scene rename Cube to Camera and it will set it to Camera.001 since the scene already has an object named Camera. With a reference to the object ob the name designated by blender will always be ob.name Keep this if you want to reference later by name.

Secondly can often do the same operations on objects without using operators, Changed the scale method below as an example of how to globally scale an object with matrices via the API.

While writing this another similar answer has been posted, suggesting much of the same Please note though the difference between setting local scale with ob.scale = (x, y, z) and using the operator, or method in code below which applies a transform. If both are run three times, on same object, with x, y, z = 2, 2, 2 the latter two will yield a global scaling of 8, 8, 8 the former only ever sets local scale to 2, 2, 2.

import bpy
from mathutils import Matrix

context = bpy.context

def sphere(x, y, z, radius, resolution=30):
    bpy.ops.mesh.primitive_uv_sphere_add(
            segments=resolution, 
            ring_count=resolution, 
            view_align=False, 
            enter_editmode=False, 
            location=(x, y, z))
    '''        
    bpy.ops.transform.resize(
            value=(radius, radius, radius), 
            constraint_axis=(False, False, False), 
            constraint_orientation='GLOBAL', 
            mirror=False, 
            proportional='DISABLED', 
            proportional_edit_falloff='SMOOTH', 
            proportional_size=1)
    '''
    scale(context.object, radius, radius, radius)
    return context.object


def scale(ob, x, y, z):
    Sx = Matrix.Scale(x, 4, (1, 0, 0))
    Sy = Matrix.Scale(y, 4, (0, 1, 0))
    Sz = Matrix.Scale(z, 4, (0, 0, 1))
    ob.matrix_world = Sx * Sy * Sz * ob.matrix_world

sphere1 = sphere(3, 0, 0, 1)
sphere1.name = "Sphere1"
# because of naming convention wont always be the same
print(sphere1.name, sphere1.name == "Sphere1")
sphere2 = sphere(0, 0, 0, 1)

scale(sphere1, 1, 2 ,1)
$\endgroup$

You must log in to answer this question.

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