4
$\begingroup$

I am trying to dynamically modify IntProperty default value. Currently, it is stored in bpy.types.Scene.mysettings as PropertyGroup. So far I've succeeded in updating the default value only by assigning a duplicated class that has been updated with new default value and pre-registered using bpy.utils.

Is there any easier way to do this? Is there a way to redeclare single property without reassigning whole class or is the solution really dynamically creating and assigning Classes?

Working example:

import bpy
from bpy.props import PointerProperty
from bpy.types import PropertyGroup, Panel

def update_default_value(self, context):
    settings = context.scene.mysettings
    # Current solution: assigning different PropertyGroup Class to update default val
    bpy.types.Scene.mysettings = PointerProperty(type=UpdatedSettings)

class AAA_PT_Demo(Panel):
    bl_space_type = "VIEW_3D"
    bl_context = "objectmode"
    bl_region_type = "UI"
    bl_label = "My Demo"
    bl_category = "Demo"

    def draw(self, context):
        settings = context.scene.mysettings
        layout = self.layout
        layout.prop(settings, 'myboolean')
        layout.prop(settings, 'myint')


class MySettings(PropertyGroup):
    myboolean : bpy.props.BoolProperty(
        name = "On Check Update Default",
        default = True,
        update = update_default_value
        )

    myint : bpy.props.IntProperty(
        name = "My Int",
        step = 5,
        default = 10,
        )

class UpdatedSettings(PropertyGroup):
    myboolean : bpy.props.BoolProperty(
        name = "On Check Update Default",
        default = True,
        update = update_default_value
        )

    myint : bpy.props.IntProperty(
        name = "My Int",
        step = 5,
        default = 999, # New Default Value
        )
        
def register():
    bpy.utils.register_class(MySettings)
    bpy.utils.register_class(UpdatedSettings)
    bpy.utils.register_class(AAA_PT_Demo)
    bpy.types.Scene.mysettings = PointerProperty(type=MySettings)

def unregister():
    bpy.utils.unregister_class(MySettings)
    bpy.utils.unregister_class(UpdatedSettings)
    bpy.utils.unregister_class(AAA_PT_Demo)
    del bpy.types.Scene.mysettings

if __name__ == "__main__":
    register()

Useful resources I found on the way:

  1. How to create PropertyGroup (original doc):
  2. How to create and access PropertyGroup (discussion)
  3. Changing default value of IntProperty
$\endgroup$

2 Answers 2

2
$\begingroup$

Use a setter getter

Instead of setting the default of the property, emulate a property with a getter setter that retrieves the custom property when set, else some default we have set.

Internal "get/set" function of property?

As an example have added another intproperty in same prop group as a means to adjust the default value without any re-registering trickery.. which can turn ugly and strip the RNA if it gets out of sync.

enter image description here Demo gif, changing setting of default is matched by myint till set

While myint has not been set it is given the default value. Once it is set it uses the value set.

import bpy
from bpy.props import PointerProperty
from bpy.types import PropertyGroup, Panel

class AAA_PT_Demo(Panel):
    bl_space_type = "VIEW_3D"
    bl_context = "objectmode"
    bl_region_type = "UI"
    bl_label = "My Demo"
    bl_category = "Demo"

    def draw(self, context):
        settings = context.scene.mysettings
        layout = self.layout
        layout.prop(settings, 'default')
        layout.prop(settings, 'myint')


class MySettings(PropertyGroup):
    def get_myint(self):
        return self.get("myint", self.default)
    
    def set_myint(self, value):
        self["myint"] = value
        
        
    default : bpy.props.IntProperty(
        name = "My Int Default",
        step = 5,
        default = 44
        )    
    myint : bpy.props.IntProperty(
        name = "My Int",
        step = 5,
        get=get_myint,
        set=set_myint,
        )

classes = (MySettings,
           AAA_PT_Demo,
           )
           
def register():
    for cls in classes:
        bpy.utils.register_class(cls)
    bpy.types.Scene.mysettings = PointerProperty(type=MySettings)

def unregister():
    for cls in reversed(classes):
        bpy.utils.unregister_class(cls)
    del bpy.types.Scene.mysettings

if __name__ == "__main__":
    register()

Other possiblilties are storing the default properties on another "toolsettings" prop group, or instead of using an annotated property def, simply declare a class variable default = 44 in the prop group and grab it from the class.

$\endgroup$
2
  • $\begingroup$ Unfortunately, this doesn't solve exact issue I am tackling. I need the default to be explicitly set to the property so it can be reset using hover on property + backspace or my operator that resets values to default. $\endgroup$
    – karlisup
    Commented Sep 24, 2020 at 8:07
  • $\begingroup$ Also, I cannot see how moving default properties to another prop group would work for the same reason mentioned above. To give a better Idea of exact usecase: user selects preset (an update fn is being triggered) and new preset values are to be set as new default values. $\endgroup$
    – karlisup
    Commented Sep 24, 2020 at 8:14
1
$\begingroup$

I ended up dynamically creating + registering a class and then reassigning it back to the same mysettings property. It works in this POC, but I have to try it in the real addon. Feel free to suggest any other solutions or make edits to this one.

import bpy
from bpy.props import PointerProperty
from bpy.types import PropertyGroup, Panel

myproperties = [
    {
        "type": "bool",
        "name": "mybool",
        "attributes": {
            "name": "On Check Update Default"
        }
    },
    { 
        "type": "int",
        "name": "myint",
        "attributes": {
            "name": "My Int",
            "default": 999
        }
    }
]


def update_default_value(self, context):
    dattributes = {}    
    for property in myproperties:
        propType = property["type"]
        propName = property["name"]
        if propType == "bool":
            dattributes[propName] = (bpy.props.BoolProperty, property["attributes"])
        elif propType == "int":
            dattributes[propName] = (bpy.props.IntProperty, property["attributes"])

    DynamicClass = type('DynamicClassName', (PropertyGroup,), {'__annotations__': dattributes})
    bpy.utils.register_class(DynamicClass)
    bpy.types.Scene.mysettings = PointerProperty(type=DynamicClass)
        


class AAA_PT_Demo(Panel):
    bl_space_type = "VIEW_3D"
    bl_context = "objectmode"
    bl_region_type = "UI"
    bl_label = "My Demo"
    bl_category = "Demo"

    def draw(self, context):
        settings = context.scene.mysettings
        layout = self.layout
        layout.prop(settings, 'mybool')
        layout.prop(settings, 'myint')


class MySettings(PropertyGroup):
    mybool : bpy.props.BoolProperty(
        name = "On Check Update Default",
        default = True,
        update = update_default_value
        )

    myint : bpy.props.IntProperty(
        name = "My Int",
        step = 5,
        default = 10,
        )
        
def register():
    bpy.utils.register_class(MySettings)
    bpy.utils.register_class(AAA_PT_Demo)
    bpy.types.Scene.mysettings = PointerProperty(type=MySettings)

def unregister():
    bpy.utils.unregister_class(MySettings)
    bpy.utils.unregister_class(AAA_PT_Demo)
    bpy.utils.unregister_class(DinamicClass) # yet not sure how to unregister dynamic class
    del bpy.types.Scene.mysettings

if __name__ == "__main__":
    register()

Again, along the way I found some really useful articles:

$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .