I’m asking your help to solve a problem I had when trying to design a custom UI.
I would like an operator to have a red background (layout.alert) or display a message, all based on the status of a boolean I’m setting (or resetting) in update functions inside properties.
Part of the code is the following:
from bpy.props import (StringProperty,
BoolProperty,
IntProperty,
FloatProperty,
FloatVectorProperty,
EnumProperty,
PointerProperty,
)
from bpy.types import (Panel,
Menu,
Operator,
PropertyGroup,
)
from bpy.utils import register_class, unregister_class
# ------------------------------------------------------------------------
# Scene Properties
# ------------------------------------------------------------------------
class IgnitProperties(bpy.types.PropertyGroup):
bool_ND1: bpy.props.BoolProperty(
update=lambda self, context: common_update(self, context, 'bool_ND1')
)
bool_ND2: bpy.props.BoolProperty(
update=lambda self, context: common_update(self, context, 'bool_ND2')
)
bool_ND4: bpy.props.BoolProperty(
update=lambda self, context: common_update(self, context, 'bool_ND4')
)
bool_ND5: bpy.props.BoolProperty(
update=lambda self, context: common_update(self, context, 'bool_ND5')
)
bool_ND6: bpy.props.BoolProperty(
update=lambda self, context: common_update(self, context, 'bool_ND6')
)
bool_ND7: bpy.props.BoolProperty(
update=lambda self, context: common_update(self, context, 'bool_ND7')
)
bool_Need_update: bpy.props.BoolProperty(
update=lambda self, context: common_update(self, context, 'bool_Need_update')
)
def common_update(self, context, origin):
if origin == 'bool_ND1':
bpy.data.objects["ND1"].hide_viewport = not getattr(self, origin)
if origin == 'bool_ND2':
bpy.data.objects["ND2"].hide_viewport = not getattr(self, origin)
if origin == 'bool_ND4':
bpy.data.objects["ND4"].hide_viewport = not getattr(self, origin)
if origin == 'bool_ND5':
bpy.data.objects["ND5"].hide_viewport = not getattr(self, origin)
if origin == 'bool_ND6':
bpy.data.objects["ND6"].hide_viewport = not getattr(self, origin)
if origin == 'bool_ND7':
bpy.data.objects["ND7"].hide_viewport = not getattr(self, origin)
if origin == 'Mis_1':
bpy.data.scenes["Scene"]["Misura_1"] = getattr(self, origin)
bool_Need_update = True
if origin == 'Mis_2':
bpy.data.scenes["Scene"]["Misura_2"] = getattr(self, origin)
bool_Need_update = True
if origin == 'Mis_3':
bpy.data.scenes["Scene"]["Misura_3"] = getattr(self, origin)
bool_Need_update = True
if origin == 'Mis_4':
bpy.data.scenes["Scene"]["Misura_4"] = getattr(self, origin)
bool_Need_update = True
if origin == 'Mis_E':
bpy.data.scenes["Scene"]["Misura_E"] = getattr(self, origin)
bool_Need_update = True
if origin == 'Mis_P':
bpy.data.scenes["Scene"]["Misura_P"] = getattr(self, origin)
if origin == 'Mis_Rot':
bpy.data.scenes["Scene"]["Centro_Rotazione"] = getattr(self, origin)
bool_Need_update = True
class MyProperties(PropertyGroup):
Mis_1: IntProperty(
name = "Misura 1",
description = "Misura 1",
default = 20,
min = 0,
max = 100,
update=lambda self, context: common_update (self, context, 'Mis_1')
)
Mis_2: IntProperty(
name = "Misura 2",
description="Misura 2",
default = 20,
min = 0,
max = 100,
update=lambda self, context: common_update (self, context, 'Mis_2')
)
Mis_3: IntProperty(
name = "Misura 3",
description="Misura 3",
default = 20,
min = 0,
max = 100,
update=lambda self, context: common_update (self, context, 'Mis_3')
)
Mis_4: IntProperty(
name = "Misura 4",
description="Misura 4",
default = 20,
min = 0,
max = 100,
update=lambda self, context: common_update (self, context, 'Mis_4')
)
Mis_E: IntProperty(
name = "Misura E",
description="Misura E",
default = 0,
min = 0,
max = 100,
update=lambda self, context: common_update (self, context, 'Mis_E')
)
Mis_P: IntProperty(
name = "Misura P",
description="Misura P",
default = 110,
min = 100,
max = 400,
update=lambda self, context: common_update (self, context, 'Mis_P')
)
Mis_Rot: IntProperty(
name = "Misura_Rot",
description="Misura Rot",
default = 0,
min = 0,
max = 60,
update=lambda self, context: common_update (self, context, 'Mis_Rot')
)
class TEST_OT_test_op(Operator):
bl_idname = 'test.test_op'
bl_label = 'Test'
bl_description = 'Test'
bl_options = {'REGISTER', 'UNDO'}
action: EnumProperty(
items=[
('Aggiorna', 'clear scene', 'clear scene'),
('Apri', 'add cube', 'add cube'),
('Chiudi', 'add sphere', 'add sphere')
]
)
def execute(self, context):
if self.action == 'Aggiorna':
self.aggiorna(context=context)
self.aggiorna_driver(context=context)
elif self.action == 'Apri':
self.apri(context=context)
self.aggiorna_driver(context=context)
elif self.action == 'Chiudi':
self.chiudi(context=context)
self.aggiorna_driver(context=context)
return {'FINISHED'}
@staticmethod
def aggiorna_driver(context):
bpy.data.objects["Attacco ND1"].animation_data.drivers[0].driver.expression += " "
bpy.data.objects["Attacco ND1"].animation_data.drivers[0].driver.expression =
bpy.data.objects["Attacco ND1"].animation_data.drivers[0].driver.expression [:-1]
bpy.data.objects["Attacco ND2"].animation_data.drivers[0].driver.expression += " "
bpy.data.objects["Attacco ND2"].animation_data.drivers[0].driver.expression =
bpy.data.objects["Attacco ND2"].animation_data.drivers[0].driver.expression [:-1]
bpy.data.objects["Attacco ND4"].animation_data.drivers[0].driver.expression += " "
bpy.data.objects["Attacco ND4"].animation_data.drivers[0].driver.expression =
bpy.data.objects["Attacco ND4"].animation_data.drivers[0].driver.expression [:-1]
bpy.data.objects["Attacco ND5"].animation_data.drivers[0].driver.expression += " "
bpy.data.objects["Attacco ND5"].animation_data.drivers[0].driver.expression =
bpy.data.objects["Attacco ND5"].animation_data.drivers[0].driver.expression [:-1]
bpy.data.objects["Attacco ND6"].animation_data.drivers[0].driver.expression += " "
bpy.data.objects["Attacco ND6"].animation_data.drivers[0].driver.expression =
bpy.data.objects["Attacco ND6"].animation_data.drivers[0].driver.expression [:-1]
bpy.data.objects["Attacco ND7"].animation_data.drivers[0].driver.expression += " "
bpy.data.objects["Attacco ND7"].animation_data.drivers[0].driver.expression =
bpy.data.objects["Attacco ND7"].animation_data.drivers[0].driver.expression [:-1]
bool_Need_update = False
@staticmethod
def aggiorna(context):
bpy.context.scene.frame_current += 1
bpy.context.scene.frame_current -= 1
@staticmethod
def apri(context):
bpy.context.scene.frame_current = 1
@staticmethod
def chiudi(context):
bpy.context.scene.frame_current = 180
class CALDIM_PT_panel(bpy.types.Panel):
bl_label = "CALDIM"
bl_category = "CAlDIM"
bl_space_type = "VIEW_3D"
bl_region_type = "UI"
def draw(self, context):
layout = self.layout
scene = context.scene
mytool = scene.my_tool
mypanel = scene.ignit_panel
layout.use_property_split = True
layout.label(text="Inserire le misure in mm")
row = layout.row()
flow = layout.grid_flow(row_major=True, columns=0, even_columns=False, even_rows=False, align=True)
col = flow.column()
col.prop(scene.my_tool, "Mis_1")
col.prop(scene.my_tool, "Mis_2")
col.prop(scene.my_tool, "Mis_3")
col.prop(scene.my_tool, "Mis_4")
col.prop(scene.my_tool, "Mis_E")
col.prop(scene.my_tool, "Mis_P")
col.prop(scene.my_tool, "Mis_Rot")
layout.prop(scene.ignit_panel, "bool_ND1", text='Visibilità ND 1')
layout.prop(scene.ignit_panel, "bool_ND2", text='Visibilità ND 2')
layout.prop(scene.ignit_panel, "bool_ND4", text='Visibilità ND 4')
layout.prop(scene.ignit_panel, "bool_ND5", text='Visibilità ND 5')
layout.prop(scene.ignit_panel, "bool_ND6", text='Visibilità ND 6')
layout.prop(scene.ignit_panel, "bool_ND7", text='Visibilità ND 7')
layout.label()
if mypanel.bool_Need_update:
layout.alert = True
layout.operator('test.test_op', text='Aggiorna').action = 'Aggiorna'
layout.label()
layout.alert = False
layout.operator('test.test_op', text='Apri').action = 'Apri'
layout.operator('test.test_op', text='Chiudi').action = 'Chiudi'
classes = (CALDIM_PT_panel,MyProperties, IgnitProperties, TEST_OT_test_op)
def register():
for cls in classes:
register_class(cls)
bpy.types.Scene.my_tool = PointerProperty(type=MyProperties)
bpy.types.Scene.ignit_panel = bpy.props.PointerProperty(type=IgnitProperties)
def unregister():
for cls in classes:
unregister_class(cls)
del bpy.types.Scene.ignit_panel
del bpy.types.Scene.my_tool
if __name__ == "__main__":
register()
Basically, I set the bool_Need_update
whenever one of the “Misura” values (1 to 4, E and Rot) are updated, and reset it into the aggiorna_driver
function, after all the scripted drivers have been updated.
Don’t know if it’s the right way to do it, but in the draw function I added the line if mypanel.bool_Need_update: layout.alert = True
but it seems to have no effect. On the other hand, if I add the instruction “layout.alert = True” it works correctly. I found this answer on stackexchange which seems to use the same approach, but has the drawback of using a checkbox to drive the appearance (in fact the modification) of a message, which is not what I want (the “Please update” warning should appear only when the bool_Need_update
boolean is set, not based on the checkbox status).
Any hint? I can upload the blender file somewhere should it be needed.
Thanks to anyone who will be willing to help.