Accessing 'Outliner Orphan Data' remove fake user button

I try to clean up the scene using unlink, but this fake user collection will stay. Pressing purge sometimes will crash blender as well, but it’s very random to reproduce the steps.

image

Hovering the button won’t show any python API as well.

Does anyone know how to access this button via python API?

Thank you.

Instead of accessing that button, you can use this in your script,

bpy.data.materials['Material Name'].use_fake_user = False

or

bpy.data.materials[MaterialID].use_fake_user = False

I think this is what the button is doing under the hood. Then you can go ahead and unlink (curious about how you are doing it).

For some reason, the outliner doesn’t immediately update after running this in the python console. But, if you switch to View Layer (or anything else) and switch back to Orphan data, the outliner seems to have updated.

Maybe, we’ll have to redraw the interface manually after the fact which I am not familiar with.

ah it seems use_fake_user = False does the trick, I’ll double check again to see if it’s work or not. Another related question to this is, How to access all data that reside in orphan data section? Like how to differentiate it with other data that currently in use?

Thank you @salaivv

I don’t think there’s a direct way to do that.

As far as I know, you should loop through each category (objects, materials, etc.) and at each iteration check if the data has no users and then append them to a list for later use, maybe.

E.g.

for mat in bpy.data.materials:
    if mat.users == 0:
        do_something()

But, of course this doesn’t work if the data in question had fake users. So, maybe,

for mat in bpy.data.materials:
    if mat.users == 0:
        do_something()
    elif mat.use_fake_user == True and mat.users == 1:
        do_something()

You might have to this for each category inside bpy.data seperately.

Be very careful when accessing datablocks by name. The name does not have to be unique. It is only unique within a single blend file, but with linking you can have “material ‘MAT’ from file X.blend” and “material ‘MAT’ from file Y.blend” linked into the same blend file.

You could shorten this to:

for mat in bpy.data.materials:
    if mat.users == mat.use_fake_user:
        do_something()

In Python True == 1 and False == 0.

1 Like

Oh, I never thought about that. Thanks for the heads up @sybren.

Yeah! I remember you were explaining this in one of your Scripting for Artist videos. From then I always tried to simplify things, but, sometimes it’s hard for me to realize that I am actually doing it. I think I ll have to mess around with more coding exercises to absorb this idea. Thanks for pointing out!

Doesn’t Python’s dictionary enforce unique keys, though? How is it possible for a dict to have more than one entry for a key? Are namespaces used at any point?

It’s not a dictionary, it’s an object that implements the __getitem__() function. You can pass a string, in which case it’ll do a lookup by name. You can also pass it a string and a library, and it’ll do a lookup based on that.

lib = bpy.data.libraries["library_name"]
material = bpy.data.materials["material_name", lib]

That will give you the material from that particular library.

Is it possible to do this such that blender references only the materials in the current blend file excluding all that are linked or overridden?

Not sure about library overrides, but the ones from the current blend file (so not linked) can be obtained by passing None as library, so bpy.data.materials["material_name", None].