Dictionary as a Custom Property

Hi,

I have a dictionary with numbers as keys like {‘2’: 4, ‘3’:6, ‘9’:18}. I would like to store it as a custom property, so that I can call it later like “my_dict[9] = 27”.

I tried it with the CollectionProperty. But there I have to add new “PropertyGroups” like my_collection.add() which gives me no possibility to define the keys on my own as far as I can see.

Can anybody see further than me? :slight_smile:

Thanks

maybe…



import bpy

# Assign a collection
class SceneSettingItem(bpy.types.PropertyGroup):
    name = bpy.props.StringProperty(name="Test Prop", default="Unknown")
    value = bpy.props.IntProperty(name="Test Prop", default=22)

bpy.utils.register_class(SceneSettingItem)

bpy.types.Scene.my_settings = \
    bpy.props.CollectionProperty(type=SceneSettingItem)

# Assume an armature object selected
print("Adding 3 values!")

prefill_dict = {2: 4, 3: 6, 9: 18}

for key in prefill_dict:
    my_item = bpy.context.scene.my_settings.add()
    my_item.name = str(key)
    my_item.value = prefill_dict[key]

for my_item in bpy.context.scene.my_settings:
    print(my_item.name, my_item.value)
    
    
print(bpy.context.scene.my_settings['2'].value)
print(bpy.context.scene.my_settings['3'].value)
print(bpy.context.scene.my_settings['9'].value)


else write a little bit more about what you want to achieve, perhaps show some failing code.

1 Like

You can still use PropertyGroups and CollectionProperties to simulate a dictionary, although it’s pretty ugly.


import bpy

from bpy.props import IntProperty, StringProperty, CollectionProperty
from bpy.types import PropertyGroup

class FooGroup(PropertyGroup):
    value = IntProperty(default=0)
    
    # we're overloading the () operator so that instance() returns self.value
    def __call__(self):
        return int(self.value)

bpy.utils.register_class(FooGroup)

bpy.types.Object.FooCollection = CollectionProperty(type=FooGroup)

obj = bpy.data.objects['Cube']
new_prop = obj.FooCollection.add()
# new_prop.name will act as a 'key' of our 'dictionary'
new_prop.name = '2'
new_prop.value = 47

 # this returns what we want; IIRC there is no way to overload instancing (i.e. no way to make a = obj.FooCollection['2'] return it's value property; you can overload __str__ but it only works with strigs (i.e. print(obj.FooCollection['2']) could print 47, but the reference to it would still reference the particular PropertyGroup - you can't shortcircuit anything
a = obj.FooCollection['2']()
print("Value {}, type {}".format(a, type(a)))
# it is also possible to use optional argument to __call__ method which sets value to whatever you supply it

# alternative is to do something like
#a = obj.FooCollection['2'].value which will return the stuff you need and also you can set it this way, but it requires adding .value each time you want to access it


There are many problems that involve this approach, but it shows that it is possible to use CollectionProperty as a some form of a dictionary. Hop it helps a little bit :slight_smile:

1 Like

do you really need a dict? What is it for? Sometimes there are other solutions which work smoothly with existing functionalities.

Thank you inside_out_ for the nice script. I’ve been rethinking my script to do it without a dict but only a list as CodemanX supposed. Thanks barFINGER for your latest post in the thread you linked. That might work as well, even though I get lost a bit in all the definitions. I tried “ob[“the_dict”] = str(the_dict)” as well, but my string got cut off.

I’m doing a script to change vertex colors based upon some calculation that takes roughly half an hour. So if I want to just change the colors I don’t want to wait another 30min but rather have my values stored in dicts/lists.

1 Like

Why not just use ID properties, i.e.


obj = bpy.context.active_object
obj['2'] = 4
obj['3'] = 6
obj['9'] = 18

?

Reading them is easy, setting them is easy - the only problem arises if you have any other ID properties that mean something but use numbers as names, or if you want multiple sets of data. Buty if one object = one set of data - ID props are simplest you’ll get.

P.S. I tried creating a custom property called DictProperty, but failed miserably. I had to use the new(cls, *p, **k) static method but failed to instanciate a DictProperty with inst = object.new(cls, *p, *k) (kinda like Catch-22 situation; it fails with TypeError: object.new(DictProperty) is not safe, use bpy_struct.new() but when I try to import bpy_struct from bpy.types, it complains that it doesn’t exist). But I get it now, you can’t write custom just in Python. That’s why they made bpy_struct inaccessible. Anyway, here’s the code, but as I said, it doesn’t work (so don’t try this at home! :))


import bpy

from bpy.types import Property

class DictProperty(Property):
    def __new__(cls, *p, **k):
        
        inst = object.__new__(cls, *p, **k)
        #inst = bpy.types.bpy_struct.__new__(cls, *p, **k)
        
        if p[0]:
            if type(p[0])==type({}):
                inst._D = p[0]
            else:
                raise Exception
        else:
            inst._D = {}

        return inst
    
    def __init__(self):
        self.me = self
        print('DictProperty:: __init__    self={}'.format(self))

    def __getattr__(self, name):
        return self._D[name]
    
    def __setattr__(self, name, value):
        self._D[name] = value

1 Like

I can also suggest like this:

import bpy

bpy.context.scene['X'] = {}

x = bpy.context.scene['X']

x['aaa'] = 123
x['bbb'] = 345

As a result you will get this:
image

Thankfully all this has been made much easier in cycles 3.0

Read the commit here (with screenshots):
https://developer.blender.org/D12435

Here is blender today with a timestamp where you can see a little bit of how its gonna work:

This is an awesome improvement :slight_smile:

You can use ast.literal_eval VS str(dict)