2
$\begingroup$

I have a Collection Property. IntProperty list_index points to the current index. There is also a list_index update function. If list_index was changed via UIList Click item I want to execute computationally expensive code. If list_index was changed programmatically from my addon, I want to avoid running this code to optimize resources.

EDIT: Added simplified addon code. The update function is called: update_func_col_index. The UIList is located in the mesh attributes panel

import time

import bpy

import bl_ui



bl_info = {
    "name": "UIList click event",
    "author": "author",
    "description": "",
    "blender": (4, 2, 0),
    "version": (0, 0, 1),
    "location": "",
    "warning": "",
    "category": "Generic"
}


from bpy.props import (IntProperty,
                       BoolProperty,
                       StringProperty,
                       CollectionProperty,
                       PointerProperty)

from bpy.types import (Operator,
                       PropertyGroup,
                       UIList,
                       Panel)




class SIMPLE_FA_result_elem(PropertyGroup):
    fa_name: StringProperty(default="")
    checked: bpy.props.BoolProperty(name="name checked", default=False)




def update_func_col_index(self, context):
    print("update_func_col_index")
    # time.sleep(5)


class SIMPLE_FA_result_one_col(PropertyGroup):
    col_index: IntProperty(default=0, update=update_func_col_index)
    col_list: CollectionProperty(
        name="Name Collection",
        type=SIMPLE_FA_result_elem)


class SIMPLE_FA_UL_result_col1(UIList):
    def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
        row = layout.row()
        row.prop(item, "fa_name")


class SIMPLE_FA_OT_result_any_add(bpy.types.Operator):
    """Add a new item to the list."""
    bl_idname = "simple_fa.result_any_add"
    bl_label = "simple_fa.result_any_add"
    bl_options = {'REGISTER', 'UNDO'}


    def execute(self, context):
        obj = context.object

        col_list=obj.simple_result_fa_col1.col_list
        item = col_list.add()

        return {'FINISHED'}


class SIMPLE_FA_OT_result_any_remove(bpy.types.Operator):
    """Add a new item to the list."""
    bl_idname = "simple_fa.result_any_remove"
    bl_label = "simple_fa.result_any_remove"
    bl_options = {'REGISTER', 'UNDO'}


    def execute(self, context):
        obj = context.object

        col=obj.simple_result_fa_col1
        col_list = col.col_list
        col_index = col.col_index
        print("col_index", col_index)
        if len(col_list) > 0:
            if col_index < 0: col_index = 0

            col_list.remove(col_index)
            if col_index > len(col_list) - 1:
                col.col_index = len(col_list) - 1


        return {'FINISHED'}

def add_ui_list_item(self, context):
    layout = self.layout
    column = layout.column()

    column.row().label(text="UIList")

    simple_result_fa_col1=context.object.simple_result_fa_col1

    column.template_list(
        listtype_name="SIMPLE_FA_UL_result_col1",
        list_id="",
        dataptr=simple_result_fa_col1,
        propname="col_list",
        active_dataptr=simple_result_fa_col1,
        active_propname="col_index",
    )

    row = layout.row()
    op = row.operator(SIMPLE_FA_OT_result_any_add.bl_idname, text="add")
    op = row.operator(SIMPLE_FA_OT_result_any_remove.bl_idname, text="remove")




ui_function_list = [add_ui_list_item]


ui_location_list = [bl_ui.properties_data_mesh.DATA_PT_mesh_attributes]
#in the mesh attributes panel


class_list = [
    SIMPLE_FA_result_elem, SIMPLE_FA_result_one_col, SIMPLE_FA_UL_result_col1, SIMPLE_FA_OT_result_any_add,  SIMPLE_FA_OT_result_any_remove
]


def register():
    for i in class_list:
        bpy.utils.register_class(i)

    bpy.types.Object.simple_result_fa_col1 = PointerProperty(type=SIMPLE_FA_result_one_col)

    for j in ui_function_list:
        for i in ui_location_list:
            i.append(j)


def unregister():
    print("----unregister----")
    for i in reversed(class_list):
        bpy.utils.unregister_class(i)

    del bpy.types.Object.simple_result_fa_col1

    for j in ui_function_list:
        for i in ui_location_list:
            i.remove(j)


if __name__ == "__main__":
    register()

```
$\endgroup$
5
  • $\begingroup$ You could maybe create a second scene property, like a BoolProperty like manual_update and then set that to True/False in whatever update function you run when you're making the change programmatically. Then before you run the computationally expensive code you check if that value in the scene is T/F before continuing. $\endgroup$
    – Jakemoyo
    Commented Aug 12 at 13:29
  • 1
    $\begingroup$ @Jakemoyo I was also thinking about this method. I was hoping there was some way to detect this automatically. Thank you. $\endgroup$
    – Vadim Tss
    Commented Aug 12 at 13:41
  • $\begingroup$ There might be a legit way to tell the difference built into the API. Not sure. but that was my first idea. $\endgroup$
    – Jakemoyo
    Commented Aug 12 at 13:56
  • $\begingroup$ @VadimTss Can you provide a simplest example of a ready-to-run script ..def update(..): ..sleep(2).. I might have an idea. $\endgroup$
    – X Y
    Commented Aug 12 at 16:36
  • $\begingroup$ @XY I have added a very shortened addon code $\endgroup$
    – Vadim Tss
    Commented Aug 12 at 17:44

1 Answer 1

0
$\begingroup$
import bpy

# this will trigger update function: update_func_col_index
bpy.context.object.simple_result_fa_col1.col_index = 1

# this will not trigger update function: update_func_col_index
bpy.context.object["simple_result_fa_col1"]["col_index"] = 1

Another method: make a function switch

def update_func_col_index(self, context):
    if update_func_col_index.enable_update is True:
        print("you enable update")
        # time.sleep(5)
    else:
        print("you disable update")
update_func_col_index.enable_update = True

# set value without update
update_func_col_index.enable_update = False
bpy.context.object.simple_result_fa_col1.col_index = 1
update_func_col_index.enable_update = True
#

Or

decoratior

def checkUpdate(func):
    def wrapper(*k, **kw):
        if checkUpdate.enable_update:
            try:
                return func(*k, **kw)
            except Exception as e:
                return print(str(e))
    
    return wrapper
checkUpdate.enable_update = True

@ checkUpdate
def update_func_col_index(self, context):
    print("come from UI")
    # time.sleep(5)

def set_prop(prop, attr, val):
    checkUpdate.enable_update = False

    try:
        setattr(prop, attr, val)
    except:
        pass

    checkUpdate.enable_update = True


# set value without update
set_prop(bpy.context.object.simple_result_fa_col1, "col_index", 1)

$\endgroup$
4
  • $\begingroup$ Thank you very much! This is what I need. To expand knowledge, could you show the code with "function switch/decoratior"? $\endgroup$
    – Vadim Tss
    Commented Aug 13 at 10:25
  • $\begingroup$ Sure, check out the new ones. $\endgroup$
    – X Y
    Commented Aug 13 at 11:19
  • $\begingroup$ Maybe there is a way to check the context or self in the function update_func_col_index, but only someone very familiar with blender source code would know if it is possible. $\endgroup$
    – X Y
    Commented Aug 13 at 11:33
  • $\begingroup$ Thank you again $\endgroup$
    – Vadim Tss
    Commented Aug 13 at 11:48

You must log in to answer this question.

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