Copied scene needs a user?

From the default startup, if you run
bpy.context.scene.copy()
you get a copy of your scene that has 0 user and gets added to the orphan data.

In reality, if you save and revert the file, your scene will still be there, even though any other orphan data would get removed if you did that.
However, for the users of my add-on, seeing an orphan scene would be confusing and worrying.

I could add a fake user but I find it unsatisfactory and still a bit confusing, as the add-on’s user may wonder what happens when trying to delete the scene and the “F” in the scene’s name is also not great because my add-on generates many scenes with long names.

So how can I make that scene not orphan without adding a fake user?

So far the only hacky workaround I found is to run any operator and then run the undo operator, which I assume does the trick because it reloads many components of the file including the scene’s user (whatever it is), but of course this is far from an ideal solution.

I’m not 100% positive, but maybe copy() isn’t supported for the Scene datablock? When auto-completing the console says “Create a copy of this data-block (not supported for all data-blocks)”, so it appears that the copy operator is more generic, and the scene isn’t linked into the main database. Perhaps someone else knows the answer to this.

If you don’t need a copy, you could try using new_scene = bpy.data.scenes.new("name") which does properly add the scene to the main database with a user.

Thanks but I need a linked copy (the equivalent of bpy.ops.scene.new(type='LINK_COPY') which is exactly what scene.copy() does and no I can’t use the operator instead.

Also, I assume that copy() is supported since the scene gets effectively copied and does get a user as soon as you reload the file or just run some operator and hit ctrl+z (which reloads many components of the file including the scene’s user, whatever it is)

@ChameleonScales This seems to be an XY Problem. Why do you need a linked copy of the scene anyway?

hehe ok I’ll explain X but I’m pretty sure Y is the only best solution here though.

So, I’m creating an add-on that generates many linked scenes (around 40 on average in my situation but variable), each of which containing its own non-shared assets. However, all scenes share many large objects (a landscape) and they also should have the same render properties and other scene-wide properties at the moment of generating them. I skipped some details but that’s the main gist of it.

I could probably work my way up from bpy.data.scenes.new() but there are so many things to copy and link that using scene.copy() takes way less code (1 line vs dozens) and probably has better performance in the end.

As for Iterating over bpy.ops.scene.new, since it jumps in each newly created scene and each one may take up to 10 seconds to load, that could be a ~400 seconds wait, whereas the scene.copy() doesn’t jump to the created scene so the loading time is ~0. There are other issues with using the operator rather than data in my situation which would require some hacks to work around but I think it’s already clear that the performance issue alone eliminates that option anyway.

Side note: your tutorials have helped me a great deal, so thank you very much!

wouldn’t it just be enough to add a fake user after the copy?

cScene = bpy.context.scene.copy()
cScene.use_fake_user = True

I explained in my first post why I’m not so fond of adding a fake user but it’s currently the better option so I’ll do that.

Why not put all the common stuff into a collection, and instance that collection into each scene? That way you can easily make changes to the collection, which are then reflected in all scenes.

Not sure I get your point as I already have the common stuff in a few collections.
Just to be sure we’re on the same line, using copy() works perfectly for me except for the fact that the new scene has 0 user, which is a minor issue considering the fact that Blender won’t delete it anyway.

I’m just wondering if this is an intended effect of using copy() or if it can be fixed upstream so the copied scene has a user by default.

I believe all the data block .copy() functions similarly. For example, try .copy()'ing the Cube object. It’ll be added to the list of data blocks as an orphan with 0 users just like your scene. It gets a “real” user once you link it into one of the scene’s collections etc.

I think the more pertinent question here is: Is there a proper way to give your .copy()'d scene a “real” user and not just a fake user. That I do not know.

The operator that copies scenes inside blender not only does a copy, but it also does some Id manipulation, and also switches to the scene proper as you’ve pointed out. I believe it’s that Id manipulation that makes it “real” when using the operator.

1 Like