1
$\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 Jan 7 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 Jan 14 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 Jan 24 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 Jan 24 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 Jan 24 at 8:42
1
$\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$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.