Getting Property groups as dictionaries
I recently reworked Mirage’s live mode to be more efficient. One of the things I wanted to improve was detecting changes in terrain settings. I figured the best way to do it was getting a dictionary of the settings and finding what changed in it.
So I wrote a little function to get all the properties inside a PropertyGroup
as a dictionary. I figure this could be useful for everyone, so here it is.
# Don't forget:
from typing import Dict
from bpy.types import PropertyGroup
def group_as_dict(group: PropertyGroup) -> Dict:
"""Get values from a property group as a dict."""
EXCLUDE = {'a_prop_I_dont_want', 'another'}
prop_dict = {}
for key in group.__annotations__.keys():
# Avoid properties we don't want
if key in EXCLUDE:
continue
# Each item in __annotations__ is a tuple
# containing the type and a dictionary
prop_type = group.__annotations__[key][0]
# Pointers to other property groups
if prop_type == PointerProperty:
prop_dict[key] = group_as_dict(getattr(settings, key))
# Store collection properties as lists
elif prop_type == CollectionProperty:
prop_dict[key] = [group_as_dict(i)
for i in getattr(settings, key)]
# BUG? IntVectorProperties don't return the Vector value on
# getattr(). Looks like we have to get it ourselves for now.
elif prop_type == IntVectorProperty:
prop_dict[key] = [i for i in getattr(settings, key)]
# Get everything else as a value
else:
prop_dict[key] = getattr(settings, key)
return prop_dict
This function is recursive. It will take nested PropertyGroups
as well as CollectionProperties
, which are returned as lists. I also added a fix for IntVectorProperties
, where getattr()
returns the pointer instead of the actual values. You could also copy that line and extend it to the other vector properties. Depending on what you would like to do with this dict, you might want a tuple or list of values instead of a Vector
object.
You can also exclude properties by adding their names to the exclude set. This is great for “UI” properties that you don’t want to store, like using enums
for tabs.
Did you find it useful? Let me know in the comments!
7 Comments
You just save my day…thank you.
Glad to hear!
Do you have perhaps info where i should look for getting into PropertyGroups and PointerProperty. I want to add this to a couple adds im doing. Im really confused of the correct approach to this
The best place to start is the API docs:
https://docs.blender.org/api/current/bpy.props.html#propertygroup-example
Basically create a propertygroup with all your props in it. Then register the class, and make a PointerProperty in bpy.types.Scene or bpy.types.Object to hold it (for example).
Came back again as i was searching for a simply method of reading dict items. I find getting the data is more complicated vs custom lists.
Ill try this code on a dict i have see if it works and i can decipher it better
Thanks for that, it was really helpful. I just had to change something because a PointerProperty isn’t necessarily another property group.
I changed the condition to
if prop_type == bpy.props.PointerProperty:
if isinstance(getattr(group, key), bpy.types.PropertyGroup):
prop_dict[key] = group_as_dict(getattr(group, key))
else:
prop_dict[key] = getattr(group, key).name
Of course it would be possible to also somehow make any object into a dictionary but that really depends on what you use it for. This is most likely something you would put in the exclude.
@lisa,
Doesn’t your method go over all pointer groups or do target a specific group when calling it?
I saw this nice single line to make a new list for changed items
Item = [item for item in dir(props) is not exclude: item
I believe this should be able to get the keys but I guess we need another loop then check items