Custom properties in the Blender Python API can be limited to setting with maximum and minimum values. In this case, the user, entering the desired value to the property field, will not be able to set a value that goes beyond the specified limits.
To set the minimum and maximum limits, we can specify the “min” and “max” parameters in our custom property and assign them the necessary limiting values.
For example, when we create a custom integer property “limited_value”, we can define limits from 0 to 25 to it:
1 2 3 4 5 6 7 |
import bpy bpy.types.Scene.limited_value = bpy.props.IntProperty( name='limited_value', min=0, max=25 ) |
Now, if we will try to set this property to a value outside the specified range, for example, “50”, the property will only be assigned the maximum possible value – “25”.
The “max” and “min” values are static. However, sometimes it is necessary to dynamically change the specified limits while the add-on is running.
If, instead of a static value, we will try to define and use a function that dynamically returns the necessary limits, for example, for the maximum value:
1 2 3 4 5 6 7 8 |
def dynamic_max(): return bpy.context.scene.frame_current bpy.types.Scene.limited_value = bpy.props.IntProperty( name='limited_value', default=0, max=dynamic_max ) |
Blender will throw an error:
TypeError: an integer is required (got type function)
ValueError: bpy_struct registration error: IntProperty could not register
This occurs because the “max” and “min” parameters only need to be set as numeric values when defining the IntPropert property.
So, we cannot set the dynamic parameters “max” and “min” directly. However, we can still achieve the desired behavior of the property by overriding its “set” and “get” functions.
First, let’s define two additional custom properties in which we will store the maximum and minimum values we need:
1 2 3 4 5 6 7 8 9 |
bpy.types.Scene.dynamic_max = bpy.props.IntProperty( name='dynamic_max', default=15 ) bpy.types.Scene.dynamic_min = bpy.props.IntProperty( name='dynamic_min', default=0 ) |
They are dynamic, and we can easily change them by placing them on the UI panel, or directly in the code:
1 |
bpy.context.scene.dynamic_min = 5 |
Let’s define the “get_limited_value” and “set_limited_value” functions. In the “set_limited_value” function, which is responsible for assigning a value to the property, we will control whether it has gone beyond the specified limits and, if it does – correct it.
1 2 3 4 5 6 7 8 9 |
def set_limited_value(self, new_value): if new_value > bpy.context.scene.dynamic_max: new_value = bpy.context.scene.dynamic_max if new_value < bpy.context.scene.dynamic_min: new_value = bpy.context.scene.dynamic_min self['limited_value'] = new_value def get_limited_value(self): return self.get('limited_value', 0) |
The “get_limited_value” function simply returns the value of the property.
Defining the property, let’s override its “get” and “set” functions with ours:
1 2 3 4 5 6 |
bpy.types.Scene.limited_value = bpy.props.IntProperty( name='limited_value', default=0, get=lambda self: get_limited_value(self), set=lambda self, value: set_limited_value(self, value) ) |
Now, each time a value is assigned to the “limited_value” property, the overridden function “set_limited_value” will be called. In this function, the value will be corrected in accordance with values specified in “dynamic_min” and “dynamic_max” properties.
Let’s show all our properties to the UI panel in the “3D Viewport” window:
1 2 3 4 5 6 7 8 9 10 11 |
class test_PT_Panel(bpy.types.Panel): bl_idname = 'TEST_PT_panel' bl_label = 'DYNAMIC MAX-MIN' bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category = 'TEST' def draw(self, context): self.layout.prop(bpy.context.scene, 'limited_value') self.layout.prop(bpy.context.scene, 'dynamic_max') self.layout.prop(bpy.context.scene, 'dynamic_min') |
Now we can set values for the “limited_value” property, which always will be limited with specified in “dynamic_min” and “dynamic_max”. At the same time, we can dynamically change these limits any way we need.
The full code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
import bpy class test_PT_Panel(bpy.types.Panel): bl_idname = 'TEST_PT_panel' bl_label = 'DYNAMIC MAX-MIN' bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category = 'TEST' def draw(self, context): self.layout.prop(bpy.context.scene, 'limited_value') self.layout.prop(bpy.context.scene, 'dynamic_max') self.layout.prop(bpy.context.scene, 'dynamic_min') # functions for get/set value with dynamic limits def set_limited_value(self, new_value): if new_value > bpy.context.scene.dynamic_max: new_value = bpy.context.scene.dynamic_max if new_value < bpy.context.scene.dynamic_min: new_value = bpy.context.scene.dynamic_min self['limited_value'] = new_value def get_limited_value(self): return self.get('limited_value', 0) def register(): bpy.types.Scene.dynamic_max = bpy.props.IntProperty( name='dynamic_max', default=15 ) bpy.types.Scene.dynamic_min = bpy.props.IntProperty( name='dynamic_min', default=0 ) bpy.context.scene.dynamic_min = 5 bpy.types.Scene.limited_value = bpy.props.IntProperty( name='limited_value', default=0, get=lambda self: get_limited_value(self), set=lambda self, value: set_limited_value(self, value) ) bpy.utils.register_class(test_PT_Panel) register() |
This code does not seem to work.
It was tested and it works.
If something wrong, you can check the errors in the system console (main menu – window – toggle system console)
Thanks for the reply.
Excuse me. I used it again and it works!
Thank you. Great script.
Yes thank you so much for supplying this