2
$\begingroup$

I'm trying to create an add-on which changes the number of bpy.types.BoolProperty dynamically along with the context by using bpy.props.CollectionProperty. But, Blender will crash when I changes the status of dynamically created checkbox.

Is there best practice to change the number of properties such as bpy.types.BoolProperty?

To help understanding what I want to do, I will show the whole my codes.

import bpy
from bpy.props import CollectionProperty, BoolProperty
from bpy_extras.io_utils import ImportHelper


bl_info = {
    "name": "Collection Property",
    "author": "nutti",
    "version": (1, 0, 0),
    "blender": (2, 91, 0),
    "location": "",
    "description": "",
    "support": "COMMUNITY",
    "category": "UV"
}

class BoolPropertyCollection(bpy.types.PropertyGroup):
    checked: BoolProperty(name="", default=True)


class TEST_OT_TestOp(bpy.types.Operator, ImportHelper):

    bl_idname = "uv.test_op"
    bl_label = "Test Op"
    bl_description = "Test Operator"
    bl_options = {'REGISTER', 'UNDO'}

    bool_prop_collection: CollectionProperty(type=BoolPropertyCollection)
    bool_prop_list = []

    def draw(self, context):
        cls = self.__class__
        layout = self.layout

        cls.bool_prop_list = []
        self.bool_prop_collection.clear()
        for i in range(200):
            item = self.bool_prop_collection.add()
            cls.bool_prop_list.append({"name": str(i), "item": item})

        for d in cls.bool_prop_list:
            layout.prop(d["item"], "checked", text=d["name"])

    def execute(self, context):
        return {'FINISHED'}
    
    def invoke(self, context, event):
        wm = context.window_manager
        wm.fileselect_add(self)

        return {'RUNNING_MODAL'}


def menu_fn(self, _):
    layout = self.layout
    layout.operator(TEST_OT_TestOp.bl_idname)


def register():
    bpy.utils.register_class(BoolPropertyCollection)
    bpy.utils.register_class(TEST_OT_TestOp)

    bpy.types.TOPBAR_MT_file_import.append(menu_fn)


def unregister():
    bpy.types.TOPBAR_MT_file_import.remove(menu_fn)

    bpy.utils.unregister_class(TEST_OT_TestOp)
    bpy.utils.unregister_class(BoolPropertyCollection)


if __name__ == "__main__":
    register()
$\endgroup$
6
  • $\begingroup$ See blender.stackexchange.com/a/182852/15543 code above would be appending to the list ad infinitum Imo the spot for this is in the invoke method . Maybe related blender.stackexchange.com/a/198926/15543 re accessing the list of selected files. $\endgroup$
    – batFINGER
    Commented Jan 7, 2021 at 12:23
  • $\begingroup$ @batFINGER These answer seems not to use CollectionProperty. I want to add elements of CollectionProperty and create a menu of them. The main problem is that the blender will be crashed when I tried to create a element of CollectionProperty dynamically. $\endgroup$
    – nutti
    Commented Jan 14, 2021 at 7:47
  • $\begingroup$ I finally found that making property must be done in invoke method not draw method. So, my issue is now resolved. $\endgroup$
    – nutti
    Commented Jan 24, 2021 at 8:13
  • $\begingroup$ Agreed, " IMO the spot for this is in the invoke method . " Posted links above, especially second, speculatively that this had something to do with selected files given the Import Helper. Can add a bool prop to the class used for files collection. Also somewhat related blender.stackexchange.com/a/160738/15543 $\endgroup$
    – batFINGER
    Commented Jan 24, 2021 at 8:28
  • $\begingroup$ btw also consider the check method as another way to dynamically populate based on some user input data. blender.stackexchange.com/questions/203747/… The invoke is run once, check many times. Apologies for missing comment. Congratulations on working it out., Consider adding an answer. $\endgroup$
    – batFINGER
    Commented Jan 24, 2021 at 8:42

1 Answer 1

3
$\begingroup$

I found that elements of bpy.types.CollectionProperty must be added in invoke method instead of draw method.

I will also post the simple code for developers who stuck at this problem.

import bpy
from bpy.props import CollectionProperty, BoolProperty, StringProperty
from bpy_extras.io_utils import ImportHelper


bl_info = {
    "name": "Collection Property",
    "author": "nutti",
    "version": (1, 0, 0),
    "blender": (2, 91, 0),
    "location": "",
    "description": "",
    "support": "COMMUNITY",
    "category": "UV"
}

class PropertyCollection(bpy.types.PropertyGroup):
    name: StringProperty(name="", default="")
    checked: BoolProperty(name="", default=True)


class TEST_OT_TestOp(bpy.types.Operator, ImportHelper):

    bl_idname = "uv.test_op"
    bl_label = "Test Op"
    bl_description = "Test Operator"
    bl_options = {'REGISTER', 'UNDO'}

    prop_collection: CollectionProperty(type=PropertyCollection)

    def draw(self, context):
        layout = self.layout

        for prop in self.prop_collection:
            layout.prop(prop, "checked", text=prop["name"])

    def execute(self, context):
        return {'FINISHED'}
    
    def invoke(self, context, event):
        wm = context.window_manager

        self.prop_collection.clear()
        for i in range(200):
            item = self.prop_collection.add()
            item.name = "Prop {}".format(i)
            item.checked = True

        wm.fileselect_add(self)

        return {'RUNNING_MODAL'}


def menu_fn(self, _):
    layout = self.layout
    layout.operator(TEST_OT_TestOp.bl_idname)


def register():
    bpy.utils.register_class(PropertyCollection)
    bpy.utils.register_class(TEST_OT_TestOp)

    bpy.types.TOPBAR_MT_file_import.append(menu_fn)


def unregister():
    bpy.types.TOPBAR_MT_file_import.remove(menu_fn)

    bpy.utils.unregister_class(TEST_OT_TestOp)
    bpy.utils.unregister_class(PropertyCollection)


if __name__ == "__main__":
    register()
$\endgroup$

You must log in to answer this question.

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