Storing custom properties in your blend file

Hi there :slight_smile:

Just ran into a slight issue, we’ve been writing an addon for a while now, its about to have its seventh release in fact. We’d been storing custom properties on the scene, in particular a unique id for the blend file that we use to locate that file amongst potentially millions of other blend files. The uuid is quite long so its able to be used as a global uuid.

Anyway, storing this as a custom property of the scene allowed it to be saved to the blend file and read by our addon each time the file was loaded by blender. The problem with this was that this assumed just one scene when in fact there can be as many as the user wants. So I started looking into finding another more general/global location to store this property so that I would not have to search through all scenes in the blend file to find it.

I noticed another addon used the window_manager to store custom properties, so I decided to give this a try, but sadly, it appears that this doesn’t work. Its a shame since there appears to only ever be one window manager, making it ideal for a place to store the uuid of the file.

So, I just wanted to check in and ask, should one be able to save custom properties to the blend file if they are appended to the window manager? I was under the impression that as long as a datablock is subclassed from bpy.types.ID, then one can use it to save custom properties to he blend file.

Note I did use the proper method for doing this which involves updating the type of the window manager rather than just dynamically placing a new property on the current window_manager object. This is the way I managed to save the uuid to the active scene prior to experimenting.

So, my question is, in summary, should I be able to save custom properties to the blend file by extending the window_manager type? If not, where else could I store custom properties that are not part of a scene, or anything that is per scene?

My alternative is to go back to using the scene, but this feels in-elegant and requires looping through all scenes to find/set the uuid to make sure its saved, and also I’d have to handle new scenes, making sure the uuid was added to them. I suppose I could just save it to one of them, but that scene could be deleted so the approach of using the scene just feels fragile.

Thanks guys :slight_smile:
James

1 Like

I also have this problem! A way to store “global” (per .blend file) addon properties would be really useful.

WindowManager properties aren’t saved with the file, so right now the only workaround is to store properties in a Scene (if they should be subject to Undo) or in a Screen (if they shouldn’t be affected by Undo). This is quite bad, since the user can unintentionally reset the addon’s properties just by deleting/creating/renaming scenes or screens.

In case Blender developers are reading this:
In my opinion, the best way to solve this would be to add to BlendData a couple of extra fields, e.g. bpy.data.addon_props (undoable) and bpy.data.addon_props_ui (not undoable), which the addons would be able to extend with their own PropertyGroup pointers.
Or, alternatively, something similar to addon preferences – i.e., an addon could register a subclass of, say, bpy.types.AddonGlobalProps, and later access it via bpy.data.addons[__name__].global_props.

1 Like

There is a differece between “The Scene” and “a Scene”. When you you use the first I assume the class object when you use the latter I assume the instance object.

Thus we have to differentiate here between a class property and an instance property. A class property as it can be found in addons/measureit/init.py

Scene.measureit_font_size = IntProperty(name="Text Size",
                                            description="Default text size",
                                            default=14, min=10, max=150)

means it registered to the class itself as such it is not tied to any individual scene. In theory even without a single scene the property should be still stored in the blender file.

if it was my_scene.measureit_font_size … my_scene being an instance object, then yes it would have been tied to a specific scene.

You could also use PropertyGroup as described here

https://b3d.interplanety.org/en/creating-blender-add-ons-variables-with-values-saved-in-blend-files/

but keep in mind that class property assigment has changed in 2.8 and is done through type hinting

(see Property registration section) https://wiki.blender.org/wiki/Reference/Release_Notes/2.80/Python_API/Addons

Hi Kilon,

Thanks for contributing to this question :slight_smile: So, we register our properties on the scene exactly how you have described above.

    @classmethod
def register(cls):
    """Add the crowd render properties to the current scene
    
    """
    #debug if needed
    #print(dir(cls))
    bpy.types.Scene.crowd_render =  PointerProperty(
            name="Crowd Render Settings",
            description="Crowd Render settings",
            type=cls
            )

So we’re already doing that. The problem we have is that the ‘value’ of the property is what is important, not just that the property definition is saved. We’ve been using the scene to store something that really describes the entire blend file and as such, storing it on a scene is prone to either clunky code or errors if the user deletes the one scene which contained the proper value.

This is the reason we’ve been looking for another place to save properties that exists independently of scenes. I’ve seen other folk use the window manager for this, but the values are not saved to the blend file.

So we’re still hoping for an answer that can work, otherwise our only fallback is to save the value to all scenes and have a handler that checks for new scenes so that the property can be written to every scene in the blend file. This approach seems really in-elegant and I’d like to avoid it.

Thanks for helping out on this thread, we’re still needing an answer though. Maybe if someone could tell us at least why the window manager doesn’t save properties like other ID blocks, that would help our brains! Better yet, suggest an alternative to writing our uuid value to each scene in the file, that would be awesome!

yeah my bad i did not realize how limited the implementation of properties was.

The only workaround I can think is to use an app.handlers.save_pre , which will register a handler and make sure your data is saved at the active scene , then you can use an app.handlers.load_post for after the blend file is loaded to restore the data. I am not sure if that will work but you can give it a try while waiting for a better answer.

1 Like

Hi @kilon, no problem :slight_smile: the save handler might be the right place to write the uuid to all scenes, that way if the user deletes one, the uuid should be preserved. The issue is complicated by the fact that the blend file will be transferred to many other computers for distributed rendering. The uuid needs to be present in the file so each render node guarantees that its loaded the correct file for the render.

I’ve looked through the bpy.data structure and can’t really say anything there seems to match what Im looking for. For a moment I thought libraries (yeah a weird choice I know) might be a good place to put this since they seem to be independent, but to invoke a new library I need to load a blend file. I can’t just instantiate a new library (or so it seems) and add it to bpy.data.Libraries. Which was a shame, since I could have added a fake lib and put a property on it?

Actually the UUID wont be needed to be present in the file if its a checksum hash. A checksum hash is a cryptographic number computed from the contents of the file. Traditionally checksum hashes use the MD5 algorithm , however SHA256 is nowadays is considered far safer. They are guaranteed to be unique and also they change dramatically even with the slightest change of the contents of a file.

If the hash is not stored with the file there will be also no way to compromise the security of the file because the file wont know which hash suppose to be the correct hash to send back to server. A server on the other hand will know, so it will only have to ask for the file on the client to checksum hash its file and this way the server will safely manage to identify that the correct file is loaded in the correct client if the hash is paired with the IP address of the client.

The cool thing about hashes is that they can be used not only for files but for data contained in those files. Blender data in general are objects inheriting from Bpy_struct which in turn is inheriting from python dictionary class. The tricky part of dictionaries is that they randomise the order of their keys, so two identical dicts can appear different hash wise. This problem can be solved using something like json.dump or other sort mechanism before hashing. This is useful if the client imports data from another blend file and you want to identify individual data as well. Again you wont need to store these IDs in the blend file as long as they are stored in your server. Most likely something you do not care about but I mention it just in case you do. However this can come handy for very large files too.

All the above is possible using standard python library so you wont need to install anything additional.

If you use a conventional ID for the file, you will have no guarantees that the file may be changed or being corrupt, which I assume you want to avoid in the first place. Of course you will need to keep track of the correct hashes on your server which I assume most likely you already doing to be able to confirm the identity of a file. Thus you don’t need to worry about saving the UUID with the file, in this case the contents of the blend file is its ID, the only thing the client will need is an addon or script that will allow it to checksum the blend file loadead. It’s just a couple lines of code that can be found in the hashlib module.


https://docs.python.org/3/library/hashlib.html

Though I agree with the idea that a checksum would be a good solution, its not worked for us in the case of blender. Blender saves its files in an interesting way by serialising the contents of system RAM and writing that to disk. There might be a bit more to that than I just described, but the situation means that if the user opens a file, then saves it, the next time the file is written to disk, its checksum will be different.

The reason for the difference is that the memory addresses are saved along with the data into the blend file. Those addresses are usually different on each run of the file. So the checksum can only really help with integrity, it can’t help with what we need which is a URI for the project so it can be located amongst others.

We do already have code which produces a unique hash for the data, that is used to verify the file has been reproduced faithfully on the other machines. But this value is never stored in the file, its generated from it.

1 Like

Agree that blend files need an established way to store meta data if the BF wants bigger companies on-board using the software.

If the main concern is the user deleting data-blocks, you could create a dummy ID, flag it as a fake user and give it the ‘dotfile’ treatment eg. naming it .meta. It would for all intents and purposes be hidden (not showing up in UI listings).

It would be interesting to know why WinMan is even considered a data-block. Apart from handling keymappings, it’s mostly runtime internals.

Hi @kaio, that sounds like a solid solution, but I’ve never before ‘created’ a custom ID, could you elaborate a little on the process or provide links to material explaining how this is done? I’ve read the blender API quite a bit, but have never even considered doing that! Thanks for the suggestion!

Whoops, I meant a sub-class of ID eg. a text data-block. Then, prefixing the name with a dot. Sorry for the confusion!