2
$\begingroup$

Appologies firstly, I'm very new to python and this site.

I have a small code that I am trying to create, to add an option on my panel addon.

The code is to select all collections in Blender and generate a Enum list, and then to populate a second Enum list with the objects within, the second list changing with the input from the first.

I plan to use the code once final to make the selection in the second menu active and show it in viewport, and to hide everything else.

So far, I have the following, but I'm pretty sure that this code will end up crashing blender as I'm pretty sure it re-writes the enum array with each redraw.

import os
import bpy
from bpy.types import (Panel, Operator)


class OBJECT_PT_Test(Panel):

    bl_label = "Panel"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = "Example"

    def draw(self, context):
        layout = self.layout
        
        row = layout.row(align=True)
        row.prop(context.scene, "test_cat", text="")
        
        row = layout.row(align=True)
        row.prop(context.scene, "test_obj", text="")

def test_cat(self, context):

    Enum_items = []
    
    for collection in bpy.data.collections:
        item = (collection.name, collection.name, "")
        Enum_items.append(item)   
    return Enum_items
    #print (Enum_items) 
    
def test_obj(self, context):
    
    obj_items = []
    
    for obj in bpy.data.collections[bpy.context.scene.test_cat].objects:
        obj_item = (obj.name, obj.name, "")
        obj_items.append(obj_item)   
    return obj_items
    #print (obj_item)  
    
def register():
    bpy.utils.register_class(OBJECT_PT_Test) 
    bpy.types.Scene.test_cat = bpy.props.EnumProperty(name= "Category Name", description= "Category Desc", items=test_cat)
    bpy.types.Scene.test_obj = bpy.props.EnumProperty(name= "Object Name", description= "Object Desc", items=test_obj)

def unregister():
    del bpy.types.Scene.test_cat
    del bpy.types.Scene.test_obj
    bpy.utils.unregister_class(OBJECT_PT_Test)
   
if __name__ == "__main__":
    register()

Any advice would be greatly appreciated and taken on board

$\endgroup$
1
$\begingroup$

Even this approach is not that beginner-friendly and there are a few concepts involved, I'd suggest use a PointerProperty for each selection instead. In case there are many collections and objects in the scene each enum gets crowded quickly and makes it difficult for the user to choose from these rather long lists.

Demo on a PointerProperty for collections as well as a PointerProperty for the objects of the selected collection using its provided poll callback function:

import bpy
from bpy.props import PointerProperty


def filter_callback(self, object):
    return object.name in self.my_collection.objects.keys()

class TEST_PT_layout_panel(bpy.types.Panel):
    bl_label = "My Panel"
    bl_category = "Test Panel"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "scene"

    def draw(self, context):
        scene = context.scene
        layout = self.layout
        
        col = layout.column()
        col.prop(scene, "my_collection")
        
        col = layout.column()
        col.enabled = True if scene.my_collection else False
        col.prop(scene, "my_collection_objects")


def register():

    bpy.types.Scene.my_collection = PointerProperty(
        name="Collection",
        type=bpy.types.Collection)
    bpy.types.Scene.my_collection_objects = PointerProperty(
        name="Object",
        type=bpy.types.Object,
        poll=filter_callback)
        
    bpy.utils.register_class(TEST_PT_layout_panel)


def unregister():
    
    bpy.utils.unregister_class(TEST_PT_layout_panel)
    del bpy.types.Scene.my_collection
    del bpy.types.Collection.my_collection_objects
    
if __name__ == "__main__":
    register()

As @batFINGER mentioned in the comments, you can also register a custom pointer to each collection type which allows to get the object reference via associated collection:

import bpy
from bpy.props import PointerProperty


def filter_callback(self, object):
    return self in object.users_collection

class TEST_PT_layout_panel(bpy.types.Panel):
    bl_label = "My Panel"
    bl_category = "Test Panel"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "scene"

    def draw(self, context):
        scene = context.scene
        layout = self.layout
        
        col = layout.column()
        col.prop(scene, "my_collection")
        
        if scene.my_collection:
            col.prop(scene.my_collection, "my_active_object")


def register():
    
    bpy.types.Scene.my_collection = PointerProperty(
        name="Collection",
        type=bpy.types.Collection)
    bpy.types.Collection.my_active_object = PointerProperty(
        name="Object",
        type=bpy.types.Object,
        poll=filter_callback)
    
    bpy.utils.register_class(TEST_PT_layout_panel)


def unregister():
    
    bpy.utils.unregister_class(TEST_PT_layout_panel)
    del bpy.types.Scene.my_collection
    del bpy.types.Collection.my_active_object
    
if __name__ == "__main__":
    register()

Related

$\endgroup$
8
  • 1
    $\begingroup$ Suggest a minor change to bpy.types.Collection.active_object = PointerProperty(type=Object) in which case can set "object of interest in collection" for each. $\endgroup$
    – batFINGER
    Aug 13 at 9:34
  • $\begingroup$ Thanks @batFINGER. Disadvantage is that data argument of Layout.prop() call cannot be None so an additional additional check is needed... However, it feels closely associated with the collection, beautiful. $\endgroup$
    – brockmann
    Aug 13 at 10:27
  • $\begingroup$ NP. Would consider using and polling on context.collection Do you know of a template ID to set the context collection|? eg can C.scene.my_collection = C.scene.collection in the console to set collection of interest (btw Watson IMO never use "my_" like in Mycroft or Mycrosoft lol) to the scene collection. The other question is how to add base collections as as an option to the selection. $\endgroup$
    – batFINGER
    Aug 13 at 10:38
  • $\begingroup$ "Can not edit built-in name" lol. Need to investigate @batFINGER $\endgroup$
    – brockmann
    Aug 13 at 10:47
  • $\begingroup$ one more thing... perhaps poll for latter could be return self in object.users_collection $\endgroup$
    – batFINGER
    Aug 13 at 10:48

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.