2

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()

nutti
  • 449
  • 4
  • 14
  • See https://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 https://blender.stackexchange.com/a/198926/15543 re accessing the list of selected files. – batFINGER Jan 07 '21 at 12:23
  • @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. – nutti Jan 14 '21 at 07:47
  • I finally found that making property must be done in invoke method not draw method. So, my issue is now resolved. – nutti Jan 24 '21 at 08:13
  • 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 https://blender.stackexchange.com/a/160738/15543 – batFINGER Jan 24 '21 at 08:28
  • btw also consider the check method as another way to dynamically populate based on some user input data. https://blender.stackexchange.com/questions/203747/addon-how-to-make-blender-run-check-redraw-when-option-changed The invoke is run once, check many times. Apologies for missing comment. Congratulations on working it out., Consider adding an answer. – batFINGER Jan 24 '21 at 08:42
  • Thanks @batFINGER. I posted my answer. – nutti Feb 04 '21 at 06:50

1 Answers1

2

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()

nutti
  • 449
  • 4
  • 14