0
$\begingroup$

I'm trying to make a python script that searches for an existing camera data-block in the scene and creates it if not found.

I've gone with a rather simple code:

for dcam in bpy.data.cameras:
     if dcam.name == "PERSP.Camera":
         break
     else:
         bpy.data.cameras.new("PERSP.Camera")

However, what I've written will keep creating increments of "PERSP.Camera" despite the condition checking if there isn't already a camera with this name.

I'm relatively new to python. Am I missing something about data-blocks where their names cannot be accessed if they're within a loop/not linked to an object in the scene?

$\endgroup$
4
  • $\begingroup$ This is a common python gotcha. You're iterating over a list while modifying it. $\endgroup$
    – Gorgious
    Commented Jun 9, 2023 at 14:55
  • $\begingroup$ I've read about python gotchas on this but I got confused as this very same code worked for bpy.data.collections. Aren't both considered Lists? Why didn't it work for cameras but worked for collections? $\endgroup$
    – Raxater
    Commented Jun 9, 2023 at 17:11
  • $\begingroup$ Thing is this code has a chance to work without you noticing it is broken, if all the conditions are met. eg if your target cam is found first then it works. Or if your target is not found, you create it, then it somehow gets placed in second place of bpy.data.cameras iterator, the code works. All other possible scenarios will make it not work as intended. $\endgroup$
    – Gorgious
    Commented Jun 9, 2023 at 17:50
  • $\begingroup$ Oh ok I see. I'll go fix the collections code as well then. Thanks a lot! $\endgroup$
    – Raxater
    Commented Jun 9, 2023 at 18:35

2 Answers 2

0
$\begingroup$

Your code is only creating the data block for the camera. You also need to create a camera object and link that to the a collection in your scene. Also, your for loop creates a new camera every time it finds a camera data block NOT named "PERSP.Camera"

You want something more like:

import bpy

col = bpy.context.collection # get active collection
test = False

for dcam in bpy.data.cameras:
    if dcam.name == "PERSP.Camera_Data":
        test = True # remember one of the camera data blocks is named PERSP.Camera_Data

if not test: # camera data block not found
    new_cam_data = bpy.data.cameras.new("PERSP.Camera_Data") # create the camera data block
    new_cam_obj = bpy.data.objects.new("PERSP.Camera", new_cam_data) # create the camera object
    col.objects.link(new_cam_obj) # link the camera object to a the active collection
$\endgroup$
0
$\begingroup$

I think you're overcomplicating things with your loop. Try something like :

import bpy

cam_name = "PERSP.Camera"
cam = bpy.data.cameras.get(cam_name)
if cam is None:
    cam = bpy.data.cameras.new(cam_name)

bpy.data.cameras acts like a dictionary. You can use get to test if an element is inside it. If not, it will return None.

You can also use the slightly more pythonic but weirder

cam = bpy.data.cameras.get(cam_name) or bpy.data.cameras.new(cam_name)

$\endgroup$
1
  • $\begingroup$ That solved most of my problems! Thanks! $\endgroup$
    – Raxater
    Commented Jun 26, 2023 at 20:24

You must log in to answer this question.

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