Для кастомных свойств в Blender Python API можно задать определенные максимальное и минимальное значения. В этом случае пользователь, вводя нужное значение в поле, не сможет указать для него величину, выходящую за указанные пределы.
Для того, чтобы задать минимальное и максимальное ограничения нужно указать в создаваемом свойстве параметры “min” и “max” и присвоить им нужные ограничивающие значения.
Например, при создании целочисленного свойства “limited_value” ему можно задать ограничения на ввод от 0 до 25 следующим образом:
1 2 3 4 5 6 7 |
import bpy bpy.types.Scene.limited_value = bpy.props.IntProperty( name='limited_value', min=0, max=25 ) |
Теперь, если попытаться задать для этого свойства значение вне указанного промежутка, например “50”, свойству будет присвоено только максимально возможное значение – “25”.
Значения “max” и “min” задаются статически. Однако иногда бывает нужно в процессе работы аддона изменить указанные пределы.
Если вместо статического значения попробовать указать функцию, динамически возвращающую нужные ограничения, например для максимального значения:
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 вернет сообщение об ошибке:
TypeError: an integer is required (got type function)
ValueError: bpy_struct registration error: IntProperty could not register
Это происходит потому, что параметры “max” и “min” при определении свойства IntPropert требуется задавать только числовыми значениями.
Задать напрямую динамические параметры “max” и “min” нельзя, однако, мы все же можем добиться нужного нам поведения свойства, переопределив для него функции “set” и “get”.
Для начала определим два дополнительных кастомных свойства, в которых будем хранить нужные нам максимальное и минимальное значения:
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 ) |
Они динамические, и мы можем их легко менять, вынеся на панель интерфейса, или напрямую в коде:
1 |
bpy.context.scene.dynamic_min = 5 |
Определим функции “get_limited_value” и “set_limited_value”. Внутри функции “set_limited_value”, которая отвечает за присваивание свойству значения будем контролировать, не выходил ли оно за заданные пределы и, если выходит, корректировать его.
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) |
Функция “get_limited_value” просто возвращает значение свойства.
При определении самого свойства, переопределим его функции “get” и “set” нашими:
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) ) |
Теперь при каждом присваивании значения свойству “limited_value” будет вызываться переопределенная нами функция “set_limited_value” в которой это значение будет корректироваться в соответствии с указанными в “dynamic_min” и “dynamic_max” лимитами.
Вынесем все три наших свойства на UI-панель в окне “3D Viewport”:
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') |
Теперь мы можем задавать значения для свойства “limited_value”, которые всегда будут лежать в указанных в “dynamic_min” и “dynamic_max” пределах, и в тоже время свободно изменять эти пределы по своему усмотрению.
Полный код:
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() |